TL;DR: ReactJS is a UI library that's a very powerful tool amongst frontend developers in building JavaScript applications. In this article, I'll introduce you to a few features coming to ReactJS.


ReactJS is a JavaScript library, built and maintained by Facebook. As of today, it powers so many popular web and mobile platforms such as Twitter, Airbnb, Lyft, Dropbox, Pinterest, Whatsapp and Instagram. The latest release of ReactJS which is React 16 ships with a lot of features such as Error Boundaries, Custom DOM Attributes definition, Fragments as return types, Portals and many others.

However, the ReactJS team is not slacking. They are hard at work looking for new ways to make React a highly performant library in UI component development. A sneak peek into new features coming to React was demoed by the creator of Redux and React core team member, Dan Abramov at JSConf Iceland, 2018. If you haven't watched Dan's talk, here is the demo.

What's coming to ReactJS?

Making it easier for developers to build great user experiences using ReactJS has always been the goal of the ReactJS team. Building for great user experiences involves splitting the attention of developers into two distinct categories:

  • Computing Power
  • Network Speed

With these categories spelt out, you start asking the following questions:

  • Are the users on a slow network? If so, how's the user experience? Can we (developers) control the loading states?
  • Are the users on a low-end device (devices with low CPU power)? If so, is using the app still a memorable experience effectively?
  • Are the users on a fast network? If so, is the experience seamless? No janky UI.
  • Are the users on a high-end device (devices with high CPU power)? If so, is the rendering flawless?

These are valid questions that need answers. Let's explore how Time Slicing and Suspense in ReactJS can help deliver the best user experience for everyone.

Time Slicing

In Dan's talk, he said: "We’ve built a generic way to ensure that high-priority updates like user input don’t get blocked by rendering low-priority updates". What does this mean? The ReactJS team named this concept Time Slicing. Let me explain in simpler terms.

"Dan Abramov: We’ve built a generic way to ensure that high-priority updates like user input don’t get blocked by rendering low-priority updates."

ReactJS is concerned about a device's CPU power. While rendering, ReactJS ensures that it doesn't block the thread thus causing the app to freeze.

Time-slicing allows ReactJS, which now runs on React Fiber, to split computations of updates on children components into chunks during idle callbacks and rendering work is spread out over multiple frames. Now, during the process of asynchronous rendering, it ensures that if a user's device is very fast, updates within the app feel synchronous and if a user's device is slow, the app feels responsive. No freezing, No janky UI experience!

Suspense

In Dan's talk, he said: "We have built a generic way for components to suspend rendering while they load asynchronous data".

The simple definition of the suspense feature is that ReactJS can pause any state update until the data been fetched is ready to be rendered. In essence, ReactJS suspends the component tree while waiting for the data to be fetched completely. During the suspension, it goes ahead to handle other high-priority updates.

"Dan Abramov: We have built a generic way for components to suspend rendering while they load asynchronous data."

Andrew Clark, (author of the suspense feature), gave a practical breakdown of how the suspense feature works in the tweets below:

Dan used an API from Future React called createFetcher (this name is likely to change) in his demo to demonstrate how the suspense feature works. The createFetcher function is a basic cache system that allows React to suspend the data fetching request from within the render method. According to Andrew Clark, it's called the simple-cache-provider.

I'll borrow a practical example from the React Apollo's team initial implementation to show you how suspense works. The React Apollo team imitated the suspense feature by suspending the result of a GraphQL query.

const MOVIE_QUERY = gql`
  query GetMovie($id: Int!) {
    movie(id: $id) {
      id
      title
      overview
      poster_path
    }
  }
`;
function MovieInfo({ movie, clearActiveResult }) {
  return (
    <Query asyncMode query={MOVIE_QUERY} variables=>
      {({ data: { movie } }) => (
        <Fragment>
          <FullPoster movie={movie} />
          <h2>{movie.title}</h2>
          <div>{movie.overview}</div>
        </Fragment>
      )}
    </Query>
  );
}

GraphQL query

In the example above, there is an asyncMode prop attached to the Query component that allows for async rendering. The sample code below shows the render method in the Query component.

....
if (loading) {
    if(this.props.asyncMode) {
        throw this.state.queryObservable!.result();
    }
}
Object.assign(data.data, this.previousData, currentResult.data);

Query component render method

If the async mode is turned on, rendering is suspended till data is fetched. In the example above, it throws a promise in async mode. During the suspension, a developer can now effectively control the loading states via using a component with a prop that has a time limit attached to it as shown below or through the loading API method from the createFetcher.

<Placeholder 
    delayMs={1000} 
    fallback={<Loadingsize="medium" color="blue" />}

Note: Dan Abramov mentioned that there is the concept of a loading API from the simple-cache-provider. It might land as a Loading component or the name might change in upcoming ReactJS releases.

In React, if a component suspends, we keep rendering the siblings as deeply as we can. Suspend means "don't commit," not "don't reconcile." - Andrew Clark

Another use case is Code splitting. Code splitting is a concept used in improving performance by loading only the code a particular page needs in an application. Code splitting works well with the suspense feature.

import { createFetcher } from 'React';

// dynamic imports
const moviePageFetcher = createFetcher(
    () => import('./MoviePage')
);

// get the MoviePage component
const moviePage = moviePageFetcher.read().default;

...

The code above loads the MoviePage component only when it is needed while taking advantage of the suspense feature via the createFetcher method.

Aside: Authenticate a React App with Auth0

We can protect our applications and APIs so that only authenticated users can access them. Let's explore how to do this with a React application using Auth0.

Auth0 login screen

We'll need an Auth0 account to manage authentication. To sign up for a free account, we can follow this link. Next, let's set up an Auth0 application and API so Auth0 can interface with a React App.

Setting Up an Auth0 Application

  1. Let's go to our Auth0 Dashboard and click the "create a new application" button.
  2. Let's call our app as "React Demo" and select "Single Page Web Applications".
  3. In the Settings for our new Auth0 application, let's add http://localhost:3000/callback to the Allowed Callback URLs.
  4. If desired, we can set up some social connections. We can then enable them for our app in the Application options under the Connections tab. The example shown in the screenshot above utilizes username/password database, Facebook, Google, and Twitter. For production, make sure to set up the correct social keys and do not leave social connections set to use Auth0 dev keys.

Set Up an API

  1. Go to APIs in your Auth0 dashboard and click on the "Create API" button. Enter a name for the API. Set the Identifier to your API endpoint URL. In this example, this is http://localhost:3001/api/. The Signing Algorithm should be RS256.
  2. You can consult the Node.js example under the Quick Start tab in your new API's settings. We'll implement our Node API in this fashion, using Express, express-jwt, and jwks-rsa.

We're now ready to implement Auth0 authentication on both our React client and Node backend API.

Dependencies and Setup

There are only two dependencies that we really need to install: auth0.js and history. To do that, let's issue npm install --save auth0-js history in the project root.

Note: As we want the best security available, we are going to rely on the Auth0 login page. This method consists of redirecting users to a login page hosted by Auth0 that is easily customizable right from the Dashboard.

After installing it, we can create an authentication service to interface with the auth0.js script. Let's call this service Auth and create it in the src/Auth/ directory with the following code:

import history from '../history';
import auth0 from 'auth0-js';

export default class Auth {
  auth0 = new auth0.WebAuth({
    // the following three lines MUST be updated
    domain: 'bkrebs.auth0.com',
    audience: 'https://bkrebs.auth0.com/userinfo',
    clientID: '3co4Cdt3h3x8En7Cj0s7Zg5FxhKOjeeK',
    redirectUri: 'http://localhost:3000/callback',
    responseType: 'token',
    scope: 'openid'
  });

  constructor() {
    this.login = this.login.bind(this);
    this.logout = this.logout.bind(this);
    this.handleAuthentication = this.handleAuthentication.bind(this);
    this.isAuthenticated = this.isAuthenticated.bind(this);
  }

  handleAuthentication() {
    this.auth0.parseHash((err, authResult) => {
      if (authResult && authResult.accessToken) {
        this.setSession(authResult);
        history.replace('/home');
      } else if (err) {
        history.replace('/home');
        console.log(err);
      }
    });
  }

  setSession(authResult) {
    // Set the time that the access token will expire at
    let expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime());
    localStorage.setItem('access_token', authResult.accessToken);
    localStorage.setItem('expires_at', expiresAt);
    // navigate to the home route
    history.replace('/home');
  }

  login() {
    this.auth0.authorize();
  }

  logout() {
    // Clear access token and expiration from local storage
    localStorage.removeItem('access_token');
    localStorage.removeItem('expires_at');
    // navigate to the home route
    history.replace('/home');
  }

  isAuthenticated() {
    // Check whether the current time is past the
    // access token's expiry time
    let expiresAt = JSON.parse(localStorage.getItem('expires_at'));
    return new Date().getTime() < expiresAt;
  }
}

The Auth service just created contains functions to deal with various steps of the sign in/sign up process. The following list briefly summarizes these functions and their descriptions:

  • handleAuthentication: looks for the result of the authentication in the URL hash. Then, process the result with the parseHash method from auth0-js;
  • setSession: sets the user's access token and the access token's expiry time;
  • login: initiates the login process, redirecting users to the login page;
  • logout: removes the user's tokens and expiry time from browser storage;
  • isAuthenticated: checks whether the expiry time for the user's access token has passed;

Besides these functions, the class contains a field called auth0 that is initialized with values extracted from the Auth0 application. Let's keep in mind that we need to update them accordingly before proceeding.

Attentive readers probably noticed that the Auth service also imports a module called history that we haven't talked about. We can define this module in only two lines, but let's define it in a file to provide reusability. Let's call this file ./src/history/history.js and add the following code:

import createHistory from 'history/createBrowserHistory'

export default createHistory()

After creating both elements, we can refactor our App component to make use of the Auth service.

import React, { Component } from 'react';
import { Navbar, Button } from 'react-bootstrap';
import './App.css';

class App extends Component {
  goTo(route) {
    this.props.history.replace(`/${route}`)
  }

  login() {
    this.props.auth.login();
  }

  logout() {
    this.props.auth.logout();
  }

  render() {
    const { isAuthenticated } = this.props.auth;

    // ... render the view
  }
}

export default App;

Note that we are passing this service through props. Therefore, when including the App component, we need to inject Auth into it: <App auth={auth} />.

Considering that we are using the Auth0 login page, our users are taken away from the application. However, after they authenticate, users automatically return to the callback URL that we set up previously (http://localhost:3000/callback). This means that we need to create a component responsible for this URL:

import React, { Component } from 'react';
import loading from './loading.svg';

class Callback extends Component {
  render() {
    const style = //...

    return (
      <div style={style}>
        <img src={loading} alt="loading"/>
      </div>
    );
  }
}

export default Callback;

This component can just contain a loading indicator that keeps spinning while the application sets up a client-side session for the users. After the session is set up, we can redirect users to another route.

Please refer to the official Quick Start Guide to see, step by step, how to properly secure a React application. Besides the steps shown in this section, the guide also shows:

Conclusion

I'm overwhelmed by the engagement of React core team members with members of the JavaScript community in landing new features in React. Worthy of note is that the APIs mentioned in this article are experimental. This is an introduction to the features that are expected to land in React soon. I'm anxious. I can't wait for these APIs to be stable.

I'll be sure to cover these features in detail and any improvements made to them when they finally land in ReactJS.

What are your thoughts on these features? Let me know in the comments section! 😊