Displaying a loading message while initializing a React component

Oct 9, 2017 using tags reactjs, redux

In non-trivial React apps, you sometimes need to load network resources prior to rendering a component in order to hide or display components within the UI. For example, it would be awkward to show a user the “Update Settings” UI if they are not logged in, and conversely the login form if they are already logged in.

The recommended place to perform these API calls is in the componentDidMount lifecycle method. In general, it’s a good idea to let the user know that processing is taking place else they end up in a very confusing state - think blank screen, browser loading-circle randomly spinning, that sort of thing. This is especially true for critical-path API calls where the result determines what is displayed on screen.

If you already use a system like redux for state management, it might be tempting to use global (redux) state variables to control the display of these “loading” elements. Take the following render example.

render() {
  const {
    isRefreshingSettings,
    isRetrievingToken,
    appToken
  } = this.props.globalState;

  if (isRefreshingSettings || isRetrievingToken) {
    return <div>Please Wait</div>;
  }

  return (
    <div>Your token is: {appToken}</div>
  );
}

Using the global isRefreshingSettings and isRetrievingToken variables, you end up with an observable flicker due to the re-render between when the token is retrieved and the settings are refreshed (fiddle available here).


Loading image with the flicker

There are a few ways to work around this. If you intend on maintaining all your state in redux, you can add additional actions that wrap your intermediary states. For the most part this technique will help you avoid the flicker but it gets tedious to maintain over time as you add and remove initialization functions.

render() {
  // ...

  // Here the fictional `isProcessingToken` state was added to wrap the token
  // validation step.
  if (isRefreshingSettings || isRetrievingToken || isProcessingToken) {
    return <div>Please Wait</div>;
  }

  // ...
}

Another way of working around the flicker is to use a local state variable to determine when initialization is complete.

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

  Promise.resolve()
    .then(() => {
      return dispatch(initiateRetrieveTokenRequest());
    })

    // ...

    })
    .then(result => {
      this.setState({ initializationComplete: true });  // <---- flip the local state variable when
                                                        // everything is complete
      console.log("Initialization complete");
    });
}

The local state variable here makes the user experience a lot smoother and contributes towards keeping your code more maintainable!


Loading image without the flicker

Resources