Adding an Auth-aware NavBar


Up until now, everything we have done has felt very technical. We have worked with Redux to manage our application's state, we have used Sagas to do interesting things such as talk to our API and decode specific responses, and we have hooked up all our routes using React Router.

In this video we will do something that feels more "front end". Something a little more visual. We are going to make ourselves a nice NavBar.

Of course, we aren't going to code up our own NavBar component. I mean, you could, but I'm not a fan of reinventing the wheel.

Instead, I am going to make use of the rather fine Reactstrap library.

Reactstrap gives us all the nice things from Bootstrap v4 but as immediately usable React components.

Now, a word of caution:

At the time of writing / recording, Reactstrap is based on Bootstrap 4 - which is still in Alpha.

A more stable implementation may be React Bootstrap, which tracks Bootstrap 3, and is largely the same except - in my opinion - more difficult to use if your needs are like mine :)

For my purposes, going with Bootstrap v4 is fine. I am making a demo app as a tutorial. If you are making a production ready site, use your own judgement.

Another way to add this may be to copy / paste the HTML directly from the Bootstrap website itself. I discourage this as then you take ownership of any JavaScript compatability issues, and honestly, with libraries like Reactstrap and React Bootstrap, these are largely solved problems.

Whichever variant you decide to use, be sure to grab the necessary CSS in whatever fashion you choose to do this. I am going to use the CDN links, as again this is a test / React tutorial project and so speed is not my most important factor, but in production it makes more sense to bundle this in to your build process, in my opinion.

<!-- /src/index.ejs -->

  <link rel="stylesheet"
        href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css"
        integrity="sha384-AysaV+vQoT3kOAXZkl02PThvDr8HYKPZhNT5h/CXfBThSRXQ6jW5DO2ekP5ViFdi"
        crossorigin="anonymous">

Next, and rather critically, we must import the right modules to make this whole thing work.

Crucially we must make sure we use the right component versions for our version of React:

// /package.json

{
  "dependencies": {
    "react": "15.4.1",
    "react-addons-css-transition-group": "15.4.1",
    "react-addons-transition-group": "15.4.1",
    "react-dom": "15.4.1"
  },
  "devDependencies": {
    "react-addons-test-utils": "15.4.1",
    "react-dom": "15.4.1"
  }
}

Of course, I have snipped out huge chunks of my package.json to highlight just the important parts here. All of these modules must match in version to whatever version of React you are using, or as they say, "you're gunna have a bad time".

Be careful with the use of carets (^) - or tildes (~) before any version numbers. These allow a wider range of versions than you may be expecting, and remember, all of these versions have to match.

Let's assume we had "react": "~15.4.1", and "react-addons-transition-group": "~15.4.1".

Essentially a caret (^) widens the scope of available versions to anything in 15.x.x. Pretty massive.

A tilde (~) is more restrictive, but even so, will still causes headaches with this setup, allowing 15.4.x.

This is exactly the sort of afternoon-losing nonsense that leads to a stressful lifestyle. It's hard to Google, and it's embarrassing to explain to the daily standup that yesterday you lost 4 hours hunting down a bad caret.

I would advise you use yarn for any package management these days, as it's a whole bunch faster that npm.

Once our dependancy issues are sorted out, we can crack on with adding a sweet Bootstrap NavBar.

Adding a Bootstrap NavBar in React

A NavBar is your classic web 'component'. Pretty much every website these days has some form of this feature.

Adding a Bootstrap navbar in React when using Reactstrap is really easy.

I'm going to modify the one from their excellent documentation:

// /src/components/NavBar.js

import React from 'react';
import {Link} from 'react-router';
import { Navbar, NavbarBrand, Nav, NavItem, NavLink } from 'reactstrap';
import '../styles/navbar.scss';

let NavBar = (props) => {

  const loginOrProfile = (auth) => {

    return auth.isAuthenticated ?
      <Nav className="float-xs-right" navbar>
        <NavItem className="navbar-text">
          Welcome back {auth.username}
        </NavItem>
        <NavItem>
          <NavLink tag={Link} to="/logout">Logout</NavLink>
        </NavItem>
      </Nav>

      :

      <Nav className="float-xs-right" navbar>
        <NavItem>
          <NavLink tag={Link} to="/login">Log in</NavLink>
        </NavItem>
      </Nav>
  };

  return (
    <div>
      <Navbar color="inverse" dark full>
        <NavbarBrand href="/">Our Cool App</NavbarBrand>
        {loginOrProfile(props.auth)}
      </Navbar>
    </div>
  );
};

NavBar.propTypes = {
  auth: React.PropTypes.object.isRequired
};

export default NavBar;

See the video for a full explanation of what's happening here.

There are some issues - auth may not actually have a username key, nor and isAuthenticated key either. We're not overly concerning ourselves with defensive measures at this stage. One step at a time!

Interestingly when using Reactstrap, be sure to use their Container,Row and Column components instead of raw div's with className's.

// /src/components/App.js

import React, { PropTypes } from 'react';
import {connect} from 'react-redux';
import { Link, IndexLink } from 'react-router';
import "babel-es6-polyfill";
import NavBar from './NavBar';
import { Container, Row } from 'reactstrap';

// This is a class-based component because the current
// version of hot reloading won't hot reload a stateless
// component at the top-level.
class App extends React.Component {
  render() {
    return (
      <div>

        <NavBar
          auth={this.props.auth}
        />

        <Container className="content-wrapper">
          <Row>
            {this.props.children}
          </Row>
        </Container>
      </div>
    );
  }
}

App.propTypes = {
  children: PropTypes.element
};

function mapStateToProps(state) {
  return {
    auth: state.auth
  }
}

export default connect(
  mapStateToProps
)(App);

Hopefully if you hadn't already, you are now starting to see the power of React with Redux, and Sagas.

Code For This Course

Get the code for this course.

Episodes