Quickstart

A quick dive into getting started with Lore

Step 4: Add Unauthorized Experience

In this step we're going to add an experience to show when the user is unauthorized.

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

Why is the API call failing?

Our API call to /user is returning a 401 because we're using a real API now, and the server can't identify who the current user is without having access to the user's token.

This API is expecting all network requests to protected endpoints to contain an Authorization header with a value of Bearer [token].

The call to /tweets is succeeding because that endpoint is public - anyone can view it.

Display an Unauthorized Experience

The fact that our application is rendering a broken experience isn't ideal. The reason this is happening is because the render() method of the Master component currently looks like this:

// src/components/Master.js
export default createReactClass({

  ...

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

    if (user.state === PayloadStates.FETCHING) {
      return (
        <div className="loader" />
      );
    }

    return (
      <div>
        <RemoveLoadingScreen />
        {React.cloneElement(this.props.children)}
      </div>
    );
  }

}
// src/components/Master.js
class Master extends React.Component {

  ...

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

    if (user.state === PayloadStates.FETCHING) {
      return (
        <div className="loader" />
      );
    }

    return (
      <div>
        <RemoveLoadingScreen />
        {React.cloneElement(this.props.children)}
      </div>
    );
  }

}
// src/components/Master.js
class Master extends React.Component {

  ...

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

    if (user.state === PayloadStates.FETCHING) {
      return (
        <div className="loader" />
      );
    }

    return (
      <div>
        <RemoveLoadingScreen />
        {React.cloneElement(this.props.children)}
      </div>
    );
  }

}

This component only has two states that it checks for:

  • If the current user is being fetched, display a loading experience.
  • If the current user is NOT being fetched, render the application.

To fix this issue we're going to add a third condition, which will be:

  • If there's an error fetching the current user, display an unauthorized experience.

To add this experience, update the render method to look like this:

// src/components/Master.js
export default createReactClass({

  ...

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

    if (user.state === PayloadStates.FETCHING) {
      return (
        <div className="loader" />
      );
    }

    if (user.state === PayloadStates.ERROR_FETCHING) {
      return (
        <div>
          <RemoveLoadingScreen />
          <h1 className="full-page-text">
            Unauthorized
          </h1>
        </div>
      );
    }

    return (
      <div>
        <RemoveLoadingScreen />
        {React.cloneElement(this.props.children)}
      </div>
    );
  }

}
// src/components/Master.js
class Master extends React.Component {

  ...

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

    if (user.state === PayloadStates.FETCHING) {
      return (
        <div className="loader" />
      );
    }

    if (user.state === PayloadStates.ERROR_FETCHING) {
      return (
        <div>
          <RemoveLoadingScreen />
          <h1 className="full-page-text">
            Unauthorized
          </h1>
        </div>
      );
    }

    return (
      <div>
        <RemoveLoadingScreen />
        {React.cloneElement(this.props.children)}
      </div>
    );
  }

}
// src/components/Master.js
class Master extends React.Component {

  ...

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

    if (user.state === PayloadStates.FETCHING) {
      return (
        <div className="loader" />
      );
    }

    if (user.state === PayloadStates.ERROR_FETCHING) {
      return (
        <div>
          <RemoveLoadingScreen />
          <h1 className="full-page-text">
            Unauthorized
          </h1>
        </div>
      );
    }

    return (
      <div>
        <RemoveLoadingScreen />
        {React.cloneElement(this.props.children)}
      </div>
    );
  }

}

With that change in place, the application will now display "Unauthorized" when there's an error fetching the current user.

The error response from the server is actually stored in tweet.error, which we could choose to display instead of the hardcoded message. But for this specific error, the server doesn't return any information in the body, so that's not an option we can use here.

Visual Check-in

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

Code Changes

Below is a list of files modified during this step.

src/components/Master.js

import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import { connect } from 'lore-hook-connect';
import PayloadStates from '../constants/PayloadStates';
import RemoveLoadingScreen from './RemoveLoadingScreen';
import '../../assets/css/main.css';

export default connect(function(getState, props) {
  return {
    user: getState('currentUser')
  };
}, { subscribe: true })(
  createReactClass({
    displayName: 'Master',

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

    childContextTypes: {
      user: PropTypes.object
    },

    getChildContext() {
      return {
        user: this.props.user
      };
    },

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

      if (user.state === PayloadStates.FETCHING) {
        return (
          <div className="loader" />
        );
      }

      if (user.state === PayloadStates.ERROR_FETCHING) {
        return (
        <div>
          <RemoveLoadingScreen />
          <h1 className="full-page-text">
            Unauthorized
          </h1>
        </div>
        );
      }

      return (
        <div>
          <RemoveLoadingScreen />
          {React.cloneElement(this.props.children)}
        </div>
      );
    }

  })
);
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'lore-hook-connect';
import PayloadStates from '../constants/PayloadStates';
import RemoveLoadingScreen from './RemoveLoadingScreen';
import '../../assets/css/main.css';

class Master extends React.Component {

  getChildContext() {
    return {
      user: this.props.user
    };
  }

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

    if (user.state === PayloadStates.FETCHING) {
      return (
        <div className="loader" />
      );
    }

    if (user.state === PayloadStates.ERROR_FETCHING) {
      return (
        <div>
          <RemoveLoadingScreen />
          <h1 className="full-page-text">
            Unauthorized
          </h1>
        </div>
      );
    }

    return (
      <div>
        <RemoveLoadingScreen />
        {React.cloneElement(this.props.children)}
      </div>
    );
  }

}

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

Master.childContextTypes = {
  user: PropTypes.object
};

export default connect(function(getState, props) {
  return {
    user: getState('currentUser')
  };
}, { subscribe: true })(Master);
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'lore-hook-connect';
import PayloadStates from '../constants/PayloadStates';
import RemoveLoadingScreen from './RemoveLoadingScreen';
import '../../assets/css/main.css';

@connect(function(getState, props) {
  return {
    user: getState('currentUser')
  };
}, { subscribe: true })
class Master extends React.Component {

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

  static childContextTypes = {
    user: PropTypes.object
  };

  getChildContext() {
    return {
      user: this.props.user
    };
  }

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

    if (user.state === PayloadStates.FETCHING) {
      return (
        <div className="loader" />
      );
    }

    if (user.state === PayloadStates.ERROR_FETCHING) {
      return (
        <div>
          <RemoveLoadingScreen />
          <h1 className="full-page-text">
            Unauthorized
          </h1>
        </div>
      );
    }

    return (
      <div>
        <RemoveLoadingScreen />
        {React.cloneElement(this.props.children)}
      </div>
    );
  }

}

export default Master;

Next Steps

Next we're going to add an Authorization header to our network requests.