A Complete Guide to Data Fetching with React Suspense

Eze Sunday
March 17, 2022

TABLE OF CONTENTS

Suspense refers to React's new ability to “suspend” rendering while components are waiting for something and display a loading indicator. React suspense allows you to write cleaner React components that are performant and are easy to maintain. This article explains what React Suspense is and How it Works.

What is Data Fetching and React Suspense?

Data fetching is a very common frontend task, right? Whether you are making an API call or loading a large array of data from a file or database, you'll need a mechanism that will allow you to effectively load the data into your frontend application.

Well, depending on the size of the data and the data fetching mechanism, this can come with some setbacks, especially from a UX perspective. Having users waiting for a longer time than expected without letting them know what is happening can lead to double-clicks, frustration, and users saying “it‘s not working”.

We can fix this. The way we solved this problem and gave the users feedback and a smooth experience was by using the old vanilla Javascript way — creating statuses in your state and updating the state with the current status of the data fetching process with the help of several of statements or ternary operators.

But React Suspense, a feature that was released as part of React 16.6, changes this approach and makes it easier to build a more friendly user interface without breaking a sweat.

So, what is React Suspense actually?

According to Reactjs.org, Suspense refers to React's new ability to “suspend” rendering while components are waiting for something and display a loading indicator.

That’s pretty straightforward, right? You’ll find this feature handy when fetching huge amounts of data or when the user's internet connection is slow, etc.

Why is React Suspense useful?

React suspense allows you to write cleaner React components that are performant and are easy to maintain — React Suspense Leverages React Lazy which mostly helps with performance improvement by delaying the loading of an object, API call response, etc until it is needed. The best part is that the user’s experience will be improved as well as It will reduce the chances of double-clicking.

What does it look like to perform data fetching operations without React Suspense? Let’s see an example of how you’d wait for a data fetching operation without React suspense.

Data fetching without React Suspense

First off, let's set up a basic React App so you can really follow up. 


Demo of what we’ll build in this tutorial



Setup ReactJS with Vite by running the command below on your Terminal:


```sh
npm init vite@latest react-suspense-app --template react
```


Follow the instruction on the Vite CLI tool and choose React as the development framework as shown below.


A basic ReactJS app template will be created for us to get started with. The directory structure should look like so:


.
├── index.html
├── package.json
├── src
│   ├── App.css
│   ├── App.jsx
│   ├── favicon.svg
│   ├── index.css
│   ├── logo.svg
│   └── main.jsx
└── vite.config.js


Run `npm install` to install the React dependencies.

Now, we are good to go.

Let’s open the `App.jsx` file and fetch users from a placeholder API.

Our first component will display a single user when fetched from the API.


`User.jsx`
```js
import { useEffect, useState } from 'react'
import './App.css'

const User = (props) => {
 return (
   <div className="card">
     <h1>{props.name}</h1>
     <p className="title">CEO & Founder, {props.company.name}</p>
     <p>Website: <a href={props.website} target="_blank" >{props.website}</a></p>
     <div style={{ margin: "24px 0" }}>
     </div>
     <p><button>Contact</button></p>
   </div>
 );

}

export default User
```


And we’ll have another component that will implement the data fetching and display the individual user components.


App.jsx
```js

function App() {
 const [status1, setStatus1] = useState(false);
 const [status2, setStatus2] = useState(false);
 const [status3, setStatus3] = useState(false);

 const [users1, setUsers1] = useState([]);
 const [users2, setUsers2] = useState([]);
 const [users3, setUsers3] = useState([]);

 useEffect(() => {
   fetch('https://jsonplaceholder.typicode.com/users')
     .then(response => response.json())
     .then(data => {

       //Fetch 1
       setTimeout(() => {
         setUsers1(data.splice(0, 3));
         setStatus1(true);
       }, 2000);

       // Fetch 2
       setTimeout(() => {
         setUsers2(data.splice(0, 3));
         setStatus2(true);
       }, 4000);

       // Fetch 3
       setTimeout(() => {
         setUsers3(data.splice(0, 3));
         setStatus3(true);
       }, 6000);

     })
 }, [])

 return (
   <>
     {status1 ?
       users1.map(user => <User
         key={user.id}
         name={user.name}
         company={user.company} website={user.website}
       />)
       :
       <h1 className='center'>Loading1...</h1>
     }

     {status2 ?
       users2.map(user => <User
         key={user.id}
         name={user.name}
         company={user.company} website={user.website}
       />)
       :
       <h1 className='center'>Loading2...</h1>
     }

     {status3 ?
       users3.map(user => <User
         key={user.id}
         name={user.name}
         company={user.company} website={user.website}
       />)
       :
       <h1 className='center'>Loading3...</h1>
     }
   </>
 );
}
```


In the above component, we’ve used the `setTimeOut` function to simulate a delay in the data fetching mechanism.

Assuming we need to fetch data from 3 different API endpoints simulated in the above code as users1, users2, and users3 and we can’t really tell how long it will take for those endpoints to return a response, we’ve set the time it takes to return a response to be 2, 4, and 6 seconds respectively for each of the requests (fetch 1, fetch 2, and fetch 3).


```js
//Fetch 1
setTimeout(() => {
   setUsers1(data.splice(0, 3));
    setStatus1(true);
}, 2000);

// Fetch 2
setTimeout(() => {
   setUsers2(data.splice(0, 3));
   setStatus2(true);
}, 4000);

// Fetch 3
setTimeout(() => {
   setUsers3(data.splice(0, 3));
   setStatus3(true);
}, 6000);
```

And we’ve also got 3 statuses to monitor the state of the data fetching process, status1, status2, and status3.

So, for each of the data coming from the API, we’ll check its status and display a loading indicator to let the user know that something is coming; otherwise, we’ll return the data from the API response.


```js
     {status1 ?
         users1.map(user => <User
         key={user.id}
         name={user.name}
         company={user.company} 
         website={user.website}
       />)
       :
       <h1 className='center'>Loading1...</h1>
     }
…

```

As you can see, there are a lot of code duplication and conditionals. This is where `React.Suspense` comes in handy —  if you want to write more readable ReactJS code with less duplication.

With React.Suspense, we will only need to wrap the User component in a Suspense component and specify a `fallback` — this fallback could be a text saying loading, an image, or even another component.


```js

       <Suspense fallback={
         <div> <img className="center"
           src="https://c.tenor.com/28DFFVtvNqYAAAAC/loading.gif"
           alt="...Loading"
         />
         </div>
       }>
         <Users />
       </Suspense>
```


So, in case your network is slow or it’s taking time to load the components, the Suspense fallback prop will be rendered. There will be no need to create multiple statuses or multiple state objects to watch the progress of your data fetching.

How do we really use React Suspense?

How to use React Suspense: Data fetching with React Suspense

It’s important to note that React Suspense is not a data fetching API like Fetch or Axios. Rather, it lets you use these tools effectively without hurting your app’s performance or annoying your users. 

Let’s convert our previous component to use React Suspense. You’ll need to use React Lazy to accomplish this task since React Lazy can allow you to render a dynamic import as a regular component. 

So, you can wrap multiple React Lazy components in one React Suspense component like so:


```js
 <Suspense fallback={ 
    <div> <img className="center" 
              src="https://c.tenor.com/28DFFVtvNqYAAAAC/loading.gif"     
              alt="...Loading" />
    </div>}>
    <People />
    <User />
<Cars />
</Suspense>
```


React Suspense will ensure that all the React Lazy components which, of course, return a promise have been fulfilled before rendering the components.

However, if you want to render the components individually, you can wrap the React Lazy components in their individual React Suspense components like so:


```js
       <Suspense fallback={<p>Loading…</p>}>
         <Users />
       </Suspense>

       <Suspense fallback={<p>Loading…</p>}>
         <People />
       </Suspense>
```


Here is a basic component code to illustrate how you’ll use React Lazy with React Suspense to achieve the desired result — I’ve attached the completed code at the end of this article. Again, we’ll use setTimeout to simulate a longer loading time. However, you’ll not need to use this approach in a real-life application.


```js
import React, { Suspense } from "react";
const Users = React.lazy(() => {
 return new Promise(resolve => {
   setTimeout(() =>
     resolve(import('./Containers/User.container.jsx')),
     6000);
 })
});

const People = React.lazy(() => {
 return new Promise(resolve => {
   setTimeout(() =>
     resolve(import('./Containers/User.container.jsx')),
     2000)
 })
});

function App() {
 return (
   <>
        <Suspense fallback={<p>Loading…</p>}>
          <Users />
       </Suspense>
       <Suspense fallback={<p>Loading…</p>}>
          <People />
       </Suspense>
   </>
 )
}
```


How React Suspense Actually Works

Notice how we imported React Lazy as a promise? Of course, React Suspense expects the child components to be resolved promises, if it isn’t resolved yet —- that means it’s still in a pending state, your fallback component— the one that says “Loading” will be rendered.

Right now, you are able to use React Suspense with React Lazy for code-splitting —- by wrapping a dynamic import in a call to `React.lazy()` which allows you to import and render only the bundle you need. In the future, when React Suspense for data fetching is released (currently experimental), it’ll allow you to cache resources and re-use them if they don’t change instead of making a new API call. When the Suspense API is fully rolled out, other Suspense-related features like SuspenseList, UseTransition, useDeferredValue will follow.

I’m really fascinated about how SuspenseList will work, let’s take a sneak pick.

React Suspense List

React SuspenseList allows you to add an ordered sequence to how your components will be rendered. So, instead of just rendering any component that is ready with the required data, you might want to render the component that has the user’s personal information first, their job, and so on.

You’ll only need to wrap all the Suspense components in the SuspenseList component in the order you want it and include the ordering prop `revealOrder` (`forwards`, `backwards`, `together` ) and a tail prop with any of the following settings (collapsed, hidden).

Forwards and backwards will reveal the components from top to bottom or bottom to top respectively. However, the together prop will reveal the components only when all of them are ready to be rendered.


```js
<SuspenseList revealOrder="forwards">
  <Suspense fallback={'Loading...'}>
    <Personal id={1} />
  </Suspense>
  <Suspense fallback={'Loading...'}>
    <Job id={2} />
  </Suspense>
  <Suspense fallback={'Loading...'}>
    <Flowers id={3} />
  </Suspense>
  ...
</SuspenseList>
```


Handling errors with React Suspense

Handling React Suspense errors is like handling errors for any other React component.

You'll need to create an Error Boundary and wrap it around your Suspense component. Let’s see an example.


`ErrorBounday.jsx`
```js
import React from "react";

export default class ErrorBoundary extends React.Component {
 constructor(props) {
   super(props);
   this.state = { error: null, errorInfo: null };
 }

 componentDidCatch(error, errorInfo) {
   this.setState({
     error: error,
     errorInfo: errorInfo,
   });

   // You can also log the error to an error reporting service
   console.error(error, errorInfo);
 }

 render() {
   if (this.state.errorInfo) {
     return <h2>Something went wrong!</h2>;
   }
   return this.props.children;
 }
}

```

The above component is an ErrorBoundary component. It’s like a try-catch block that catches all the errors and returns a friendly error message without breaking the entire application.

This is how you’ll use it in your component.


<ErrorBoundary>
     <Suspense fallback={<p>Loading…</p>}>
           <People />
      </Suspense>
</ErrorBoundary>


You can also use this Error Boundary npm package to catch your errors. You can pass a fallback UI to an Error Boundary so that you can return a specific message or UI for that error.

If you'd like to check the entire code, I have created a GitHub repository that contains all the code samples we’ve used in our illustration. You can check it out here.

Conclusion

React Suspense is a useful toolkit for frontend development. The best part is that it’s easy to implement and the benefit is outstanding for both your users and your dev team. It’s definitely worth trying in case you haven’t used it before. 

Happy coding!