Quickstart

A quick dive into getting started with Lore

Step 6: Add Logout Page

In this step we're going to add a logout experience.

You can view the finished code for this step by checking out the authentication.6 branch of the completed project.

Create the Logout Component

While we don't have a "logout page" in a visual sense, we still have behavior that we want executed when the user logs out, such as removing their user token and redirecting them to the login page.

We're going to store this behavior in a component so that we can have it execute when the user navigates to the /logout route.

Start by creating a Logout component:

lore generate component Logout

Then update the Logout component to look like this:

import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import auth from '../utils/auth';
import ShowLoadingScreen from './ShowLoadingScreen';

export default createReactClass({
  displayName: 'Logout',

  propTypes: {
    router: PropTypes.object.isRequired
  },

  componentDidMount() {
    const { router } = this.props;

    auth.deleteToken();
    router.push('/');
  },

  render() {
    return (
      <ShowLoadingScreen/>
    );
  }

});
import React from 'react';
import PropTypes from 'prop-types';
import auth from '../utils/auth';
import ShowLoadingScreen from './ShowLoadingScreen';

class Logout extends React.Component {

  componentDidMount() {
    const { router } = this.props;

    auth.deleteToken();
    router.push('/');
  }

  render() {
    return (
      <ShowLoadingScreen/>
    );
  }

}

Logout.propTypes = {
  router: PropTypes.object.isRequired
};

export default Logout;
import React from 'react';
import PropTypes from 'prop-types';
import auth from '../utils/auth';
import ShowLoadingScreen from './ShowLoadingScreen';

class Logout extends React.Component {

  static propTypes = {
    router: PropTypes.object.isRequired
  };

  componentDidMount() {
    const { router } = this.props;

    auth.deleteToken();
    router.push('/');
  }

  render() {
    return (
      <ShowLoadingScreen/>
    );
  }

}

export default Logout;

Add the /logout route

Next import the Logout component into routes.js and update the routes to look like this:

// routes.js
...
import Logout from './src/components/Logout';

export default (
  <Route>
    <Route path="/login" component={Login} />
    <Route path="/logout" component={Logout} />
    <Route path="/auth/callback" component={AuthCallback} />

    ...
  </Route>
);

Convert Logout Button to Link

Finally, we need to make it so that when the user clicks the Logout button in the Profile component they are redirected to the /logout route, which will delete their user token and redirect them to the login page.

Locate the Logout button in your Profile component:

// src/components/Profile.js
<button className="btn btn-primary">
  Log out
</button>

Convert this button to a React Router Link and have it point to /logout:

// src/components/Profile.js
import { Link } from 'react-router';
...
  render() {
    ...
      <Link className="btn btn-primary" to="/logout">
        Log out
      </Link>
    ...
  }
...

With this change in place, clicking the Logout button will redirect you to /logout, and once you log in, you'll be redirected to the main application.

Visual Check-in

If everything went well, your application should now look like this.

Code Changes

Below is a list of files modified during this step.

src/components/Logout.js

import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import auth from '../utils/auth';
import ShowLoadingScreen from './ShowLoadingScreen';

export default createReactClass({
  displayName: 'Logout',

  propTypes: {
    router: PropTypes.object.isRequired
  },

  componentDidMount() {
    const { router } = this.props;

    auth.deleteToken();
    router.push('/');
  },

  render() {
    return (
      <ShowLoadingScreen/>
    );
  }

});
import React from 'react';
import PropTypes from 'prop-types';
import auth from '../utils/auth';
import ShowLoadingScreen from './ShowLoadingScreen';

class Logout extends React.Component {

  componentDidMount() {
    const { router } = this.props;

    auth.deleteToken();
    router.push('/');
  }

  render() {
    return (
      <ShowLoadingScreen/>
    );
  }

}

Logout.propTypes = {
  router: PropTypes.object.isRequired
};

export default Logout;
import React from 'react';
import PropTypes from 'prop-types';
import auth from '../utils/auth';
import ShowLoadingScreen from './ShowLoadingScreen';

class Logout extends React.Component {

  static propTypes = {
    router: PropTypes.object.isRequired
  };

  componentDidMount() {
    const { router } = this.props;

    auth.deleteToken();
    router.push('/');
  }

  render() {
    return (
      <ShowLoadingScreen/>
    );
  }

}

export default Logout;

routes.js

import React from 'react';
import { Route, IndexRoute, Redirect } from 'react-router';

/**
 * Wrapping the Master component with this decorator provides an easy way
 * to redirect the user to a login experience if we don't know who they are.
 */
import UserIsAuthenticated from './src/decorators/UserIsAuthenticated';

/**
 * Routes are used to declare your view hierarchy
 * See: https://github.com/ReactTraining/react-router/blob/v3/docs/API.md
 */
import Master from './src/components/Master';
import Layout from './src/components/Layout';
import Feed from './src/components/Feed';
import Login from './src/components/Login';
import AuthCallback from './src/components/AuthCallback';
import Logout from './src/components/Logout';

export default (
  <Route>
    <Route path="/login" component={Login} />
    <Route path="/logout" component={Logout} />
    <Route path="/auth/callback" component={AuthCallback} />

    <Route component={UserIsAuthenticated(Master)}>
      <Route path="/" component={Layout}>
        <IndexRoute component={Feed} />
      </Route>
    </Route>
  </Route>
);

src/components/Profile.js

import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import { Link } from 'react-router';

export default createReactClass({
  displayName: 'Profile',

  propTypes: {
    user: PropTypes.object.isRequired
  },

  getDefaultProps() {
    return {
      user: {
        id: 1,
        data: {
          nickname: 'ayla',
          avatar: 'https://cloud.githubusercontent.com/assets/2637399/19027069/a356e82a-88e1-11e6-87d8-e3e74f55c069.png'
        }
      }
    };
  },

  render() {
    const { user } = this.props;

    return (
      <div className="card profile">
        <div className="card-block">
          <img
            className="img-circle avatar"
            src={user.data.avatar} />
          <h4 className="card-title">
            Hi {user.data.nickname}!
          </h4>
          <div className="card-text">
            <p>You have permission to perform the following:</p>
            <ul className="permissions">
              <li>Create Tweets</li>
              <li>Edit your own tweets</li>
              <li>Delete your own tweets</li>
            </ul>
          </div>
          <Link className="btn btn-primary" to="/logout">
            Log out
          </Link>
        </div>
      </div>
    );
  }

});
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';

class Profile extends React.Component {

  render() {
    const { user } = this.props;

    return (
      <div className="card profile">
        <div className="card-block">
          <img
            className="img-circle avatar"
            src={user.data.avatar} />
          <h4 className="card-title">
            Hi {user.data.nickname}!
          </h4>
          <div className="card-text">
            <p>You have permission to perform the following:</p>
            <ul className="permissions">
              <li>Create Tweets</li>
              <li>Edit your own tweets</li>
              <li>Delete your own tweets</li>
            </ul>
          </div>
          <Link className="btn btn-primary" to="/logout">
            Log out
          </Link>
        </div>
      </div>
    );
  }

}

Profile.propTypes = {
  user: PropTypes.object.isRequired
};

Profile.defaultProps = {
  user: {
    id: 1,
    data: {
      nickname: 'ayla',
      avatar: 'https://cloud.githubusercontent.com/assets/2637399/19027069/a356e82a-88e1-11e6-87d8-e3e74f55c069.png'
    }
  }
};

export default Profile;
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';

class Profile extends React.Component {

  static propTypes = {
    user: PropTypes.object.isRequired
  };

  static defaultProps = {
    user: {
      id: 1,
      data: {
        nickname: 'ayla',
        avatar: 'https://cloud.githubusercontent.com/assets/2637399/19027069/a356e82a-88e1-11e6-87d8-e3e74f55c069.png'
      }
    }
  };

  render() {
    const { user } = this.props;

    return (
      <div className="card profile">
        <div className="card-block">
          <img
            className="img-circle avatar"
            src={user.data.avatar} />
          <h4 className="card-title">
            Hi {user.data.nickname}!
          </h4>
          <div className="card-text">
            <p>You have permission to perform the following:</p>
            <ul className="permissions">
              <li>Create Tweets</li>
              <li>Edit your own tweets</li>
              <li>Delete your own tweets</li>
            </ul>
          </div>
          <Link className="btn btn-primary" to="/logout">
            Log out
          </Link>
        </div>
      </div>
    );
  }

}

export default Profile;

Next Steps

Next we're going to add an endpoint to the mock API that we can use to retrieve the current user.