Asynchronous rendering with React

How the new React Suspense API might reshape the way we build components

Tue Nov 06 2018 - 3 min read

Since I’ve started playing with React a couple of years ago, I’ve always been a big fan of functional components. Their simplicity and conciseness make them easy to read and test. The problem though was that, until now, there was no way to do asynchronous calls which is the crucial part of most applications in the real world, so classes were always the way to go.
However, starting from React 16.6.0 and the new Suspense API, this is not an issue anymore, functional components can now perform asynchronous calls and render data that comes from them. In this post, I’m going to show you an up to date example so you can easily test the Suspense API.

Note: Although it’s available through the last official version of React, using Suspense as I’ll show you in this post is not yet intended for production. This example solely exists as an experiment.

What is Suspense in a nutshell?

Suspense basically suspends the rendering of a component while loading data from a cache. This means that our component will only show up once the whole tree is ready. If the data we’re trying to render is not in the cache, the cache throws a Promise. When the promise resolves, the rendering continues. 
While all of this is happening, Suspense will render a fallback component which could be for example a loading indicator, a message, or anything we usually render in our applications to signal the user that something asynchronous is happening.

A new way to build components

As of today, when we want to render a component that shows some data coming from an asynchronous call in React, we’re stuck with classes. We have to use the component lifecycle methods to ensure the call happens on mount, and use the local state to manage the loading state. We can see below a small example of a pattern that I’m sure pretty much every React developer had to follow:

1// Code snippet 1: "React component doing an asynchronous call before rendering the data implemented using a Class."
2import React, { Component, Fragment } from 'react';
3
4class ClassicAsync extends Component {
5 constructor(props) {
6 super(props);
7 this.state = { loading: false, title: null };
8 }
9
10 componentDidMount() {
11 fetch('https://jsonplaceholder.typicode.com/todos/')
12 .then(response => response.json())
13 .then(json => this.setState({ loading: false, data: json }));
14 }
15
16 renderList = data => {
17 return (
18 <ul>
19 {data.map(item => (
20 <li style={{ listStyle: 'none' }} key={item.id}>
21 {item.title}
22 </li>
23 ))}
24 </ul>
25 );
26 };
27
28 render() {
29 const { loading, data } = this.state;
30
31 return (
32 <Fragment>
33 <h2 style={{ textAlign: 'center' }}>
34 {`React: ${React.version} Demo`}
35 </h2>
36 {loading ? 'Classic loading placeholder' : this.renderList(data)}
37 </Fragment>
38 );
39 }
40}
41
42export default ClassicAsync;

How Suspense changes that? Well, quite a lot actually if you compare the code above with the following one:

1// Code Snippet 2: React component doing an asynchronous call before rendering the data implemented using React Suspense.
2import React, { Suspense, Fragment } from 'react';
3
4// Fetcher code goes here
5const getDate = () => Fetcher.read();
6
7const List = () => {
8 const data = getData();
9 return (
10 <ul>
11 {data.map(item => (
12 <li style={{ listStyle: 'none' }} key={item.id}>
13 {item.title}
14 </li>
15 ))}
16 </ul>
17 );
18};
19
20const App = () => (
21 <Fragment>
22 <h2>{`React: ${React.version} Demo`}</h2>
23 <Suspense fallback={<div>Loading...</div>}>
24 <List />
25 </Suspense>
26 </Fragment>
27);

As we can see with this example: no more class! Suspense is managing for us the loading state through the fallback prop, which is rendered until List is ready to be rendered, that is when the dependent asynchronous call resolves and returns the data. However, this is only a partial example. As stated in the first part, the rendering of List in this example is suspended while loading data from a cache, which is what the Fetcher function is all about.

Using react-cache

This is key for getting the example above to work. The caching part is needed for Suspense to read the data from the asynchronous call.
Before diving in the details, let’s look at how the Fetcher function is implemented for our example:

1// Code Snippet 3: Fetcher resource implementation using functions from react-cache
2import { unstable_createResource } from 'react-cache';
3
4const Fetcher = unstable_createResource(() =>
5 fetcher('https://jsonplaceholder.typicode.com/todos').then(r => r.json())
6);

For this post I used react-cache. It’s a package made by the React core team that provides a basic cache that is going to store asynchronous data, like the data that we’re getting once our fetch call resolves, and allows us to access that data asynchronously. In the code snippet above we basically use the unstable_createResource function where we pass our asynchronous call, which will initiate a cache and store the resulting data into it. Accessing that data from the cache is done through the read function as we can see in the Code Snippet 2. However, this way of doing caching is currently not meant to be used in production (the React team emphasized this quite a bit in the README of this repository).

Full Example

Here’s the full example for this article:

1// Code Snippet 4: The full example of a functional React component using the Suspense API
2import React, { Suspense, Fragment } from 'react';
3import { unstable_createResource } from 'react-cache';
4
5const Fetcher = unstable_createResource(() =>
6 fetcher('https://jsonplaceholder.typicode.com/todos').then(r => r.json())
7);
8
9const getDate = () => Fetcher.read();
10
11const List = () => {
12 const data = getData();
13 return (
14 <ul>
15 {data.map(item => (
16 <li style={{ listStyle: 'none' }} key={item.id}>
17 {item.title}
18 </li>
19 ))}
20 </ul>
21 );
22};
23
24const App = () => (
25 <Fragment>
26 <h2>{`React: ${React.version} Demo`}</h2>
27 <Suspense fallback={<div>Loading...</div>}>
28 <List />
29 </Suspense>
30 </Fragment>
31);

I made this example available in a Github repository based on create-react-app so you can also give it a try and experiment with Suspense very quickly!

I really can’t wait for this pattern to be ready for production. Combining Suspense and the recently announced React hooks is getting us closer to building React apps fully based on functional components. If you want to learn more about Suspense here’s a really complete summary in a tweet from a member of the React team:

What to read next?
If you want to read more about React or frontend development, you can check the following articles:

If you liked this article, don't forget to share it or click here to leave a comment discuss about it on Twitter. Do you have any questions, comments or simply wish to contact me privately? I’m always reachable on Twitter or on my website. Do not hesitate to contact me!


Have a wonderful day.
Maxime

© 2019 Maxime Heckel. Made in SF.