Spencer FengSpencer Feng

Authentication in React Apps with JWT: Part 8

Welcome to the eighth tutorial in our ‘Authentication and Authorisation in React Apps with JSON Web Tokens’ series. Currently, the logout link does nothing in our application. In this tutorial, we will add the function to log a logged-in user out which includes tasks:

  • Clear the JWT token in the localStorage and empty the cUser object in the state
  • Redirect the user to the home page

Clear the JWT token in the localStorage and empty the cUser object in the state

Once the logout link is clicked, the token in the localStorage needs to be cleared, so that it can no longer be used to authenticate a user. Let’s update the code in ‘client/src/Navbar.js’ to the code below (changes are highlighted):

import React, { Component } from 'react';
import { IsEmpty } from '../helpers';
import { Link } from 'react-router-dom';

class Navbar extends Component {
    constructor(props) {
        super(props);
        this.logout = this.logout.bind(this);
    }

    logout(e) {
        e.preventDefault();

        // set store state cUser to be an empty object
        this.props.removeLoggedInUser();

        // remove the token in localStorage
        localStorage.removeItem('token');
    }

    render() {

        return (

            <nav className="navbar navbar-inverse navbar-fixed-top">
                <div className="container">
                    <div className="navbar-header">
                        <button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                            <span className="sr-only">Toggle navigation</span>
                            <span className="icon-bar"></span>
                            <span className="icon-bar"></span>
                            <span className="icon-bar"></span>
                        </button>
                        <a className="navbar-brand" href="#">React JWT</a>
                    </div>
                    <div id="navbar" className="collapse navbar-collapse">
                        <ul className="nav navbar-nav">
                            <li className="active"><Link to='/'>Home</Link></li>
                        </ul>
                        <ul className="nav navbar-nav navbar-right">
                            {IsEmpty(this.props.cUser) && 
                                <li><Link to='/login'>Login</Link></li>
                            }
                            {IsEmpty(this.props.cUser) &&
                                <li><Link to='/signup'>Signup</Link></li>
                            }
                            {!IsEmpty(this.props.cUser) &&
                                <li className="dropdown">
                                    <a className="dropdown-toggle" href="#" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
                                        Hello, {this.props.cUser.firstName} {this.props.cUser.lastName} <span className="caret"></span>
                                    </a>
                                    <ul className="dropdown-menu">
                                        <li><a href="#" onClick={this.logout}>Logout</a></li>
                                    </ul>
                                </li>
                            }
                        </ul>
                    </div>
                </div>
            </nav>

        )

    }

}

export default Navbar;

The changes are obvious. We added an event listener to handle the click event of the logout link. In the event handler, we called the ‘removeLoggedInUser()’ function which is passed in as a component prop to empty the logged-in user and then we clear the token in the local storage.

As we mentioned above, we need a ‘removeLoggedInUser()’ function to empty the logged-in user. Let’s open the ‘client/src/components/NavbarContainer.js’ file and update it to the code below (changes are highlighted):

import React from 'react';
import { connect } from 'react-redux';
import Navbar from './Navbar';
import { setAuthenticatedUser } from '../actions';

const mapStateToProps = (state, ownProps) => ({
    cUser: state.cUser
});

const mapDispatchToProps = (dispatch) => ({
    removeLoggedInUser: () => { dispatch(setAuthenticatedUser({})); }
});

export default connect(mapStateToProps, mapDispatchToProps)(Navbar);

The changes we made to this file allow us to pass a function which dispatches the ‘setAuthenticatedUser’ action to the presentational component as a prop. When this action is dispatched, the cUser state in the store becomes an empty object.

Redirect the user to the home page

We want to redirect users to the home page after they logged out. But as you can see, the logout link is defined in the ‘Navbar’ component which is not a “route component “, meaning it is not rendered like so: <Route path=”/your-path” component={YourComponent} />, so it does not have the access to the Router history object which we need to preform the redirection like a “route component”. In order to allow the ‘Navbar’ component to have the access to the Router history object, we need to use the ‘withRouter’ function in react-router. If you want to learn more about it, please read the documentation here.

Let’s update the ‘client/src/components/NavbarContainer’ to the code below (changes are highlighted):

import React from 'react';
import { connect } from 'react-redux';
import Navbar from './Navbar';
import { setAuthenticatedUser } from '../actions';
import { withRouter } from 'react-router-dom';

const mapStateToProps = (state, ownProps) => ({
    cUser: state.cUser
});

const mapDispatchToProps = (dispatch) => ({
    removeLoggedInUser: () => { dispatch(setAuthenticatedUser({})); }
});

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Navbar));

Then add the following code to the bottom of the ‘logout’ function in ‘client/src/components/Navbar.js’ file:

// redirect to home page
this.props.history.push('/');

In order to test if the redirection work, let’s create a page component ‘client/src/components/MemberNewsPage.js’ with the contents below:

import React, { Component } from 'react';

class MemberNewsPage extends Component {

    render() {

        return (

            <div className="row">
                <div className="col col-md-3"></div>
                <div className="col col-md-6">
                    <div className="panel panel-info">
                        <div className="panel-heading">Member News</div>
                        <div className="panel-body">
                            <ul>
                                <li>Member news 1</li>
                                <li>Member news 2</li>
                                <li>Member news 3</li>
                                <li>Member news 4</li>
                                <li>Member news 5</li>                    
                            </ul>
                        </div>
                    </div>
                </div>
                <div className="col col-md-3"></div>
            </div>

        );

    }

}

export default MemberNewsPage;

Let’s import the page component to the ‘client/src/App.js’ file:

import MemberNewsPage from './components/MemberNewsPage';

Then, we add it to the route.

<Route path="/" component={HomePage} />

Now, if we login to the application and then go to the member news page and then click on the logout link, you can see that we can logout the application and then we will be redirected to the home page.

Great, that is all we need to cover in this tutorial. In the next tutorial in this series, we will cover how to prevent logged-in users from accessing login and logout pages. See you next time!

View the source code for this tutorial on GitHub.

Please sign up to our Newsletter if you want to get notified when new tutorials are available.

×