Data fetching with Suspense in React
Tags: javascript, webdev, react, suspense
Published: 2022-07-11 23:11:20

by [Marvel Ken](https://blog.openreplay.com/authors/marvel-ken)

Suspense allows us to suspend component rendering. Suspense in React is not a library or server state manager; it's literally what the word Suspense entails, a feature for managing asynchronous operations in a React app, keeping things in suspense until data are ready. 

In this article, we will look at how Suspense works by fetching data from an API and rendering it in our application. In the course of this article, we will use [Jsonplaceholder](https://jsonplaceholder.typicode.com/) for our API.

## Why Suspense?

Suspense aims to help with handling async operations by letting you wait for a code to load. It's needed because users need their eyes fed with something like a spinner, so they know they expect data to be displayed. Concisely, Suspense will defer the execution of a component's tree until the Promise is either resolved or rejected. Sites without proper handling of asynchronous operations are considered wretched sites.

How do we fetch data without Suspense? The following code is typical:

```javascript
const [lyrics, isLoading] = fetchData("/lyrics");
if (isLoading) {
  return <Spinner />;
}
return <Lyrics data={lyrics} />;
```

A variable `isLoading` is used to track the `status` of the request. If it's true, we render a `spinner`. The spinner is used for a better user interface, enabling the user to know that data is being fetched. There's absolutely nothing wrong if we do it this way, but there are better ways of getting this done. Let's see the simplest use case of Suspense:
 
```javascript
const lyrics = fetchData("/lyrics");
return (
  <Suspense fallback={<Spinner />}>
    <Lyrics data={lyrics} />
  </Suspense>
);
```

These are the changes that Suspense brought to the way we handle network calls:

- Instead of doing it by hand, we have Suspense rendering a fallback (spinner) declaratively.
- React didn't know there was a network call, so we had to manage the loading state by ourselves. Using Suspense, React identifies that a network call is made and running.
- By wrapping the Lyrics component with Suspense, it suspends rendering data until the network call is done.


How does React know that a network call was made and is pending, is React that smart? Suspense renders a fallback component, but in no place in the code do we communicate to React that we are making a network request. This is where data fetching libraries such as Axios come into play. For the benefit of this tutorial, we will use Axios to communicate the loading state to React.

## Data fetching using Suspense

Create a folder,  head into your text editor, open your terminal and run the below commands;

```
npx create-react-app suspense
cd suspense 
npm install react@rc react-dom@rc --save   //we need to manually do it this way because Suspense is not yet stable. 
npm install axios --save
npm start
```

To handle data fetching, we will need a folder; I named mine `FetchApi`. This folder will have two files: `Fetch.js` and `WrapPromise.js`. These files will be responsible for fetching data from our API and communicating to Suspense. We will discuss more on this as we progress in the article for a better understanding.

### Fetch.Js

We will use [Axios](https://axios-http.com/docs/intro) to fetch Data. Once a request is made, Axios returns a promise that is fulfilled or declined; this depends on the backend server response. We will need the user's profile from [jsonplaceholder](https://jsonplaceholder.typicode.com/). We will have our asynchronous function this way:

```javascript
import axios from "axios";

export async function fetchUser() {
  return axios
    .get("https://jsonplaceholder.typicode.com/users/3")
    .then((res) => res.data)
    .catch((err) => console.log(err));
}
```

We have exported our asynchronous function so that we can have access to it in our `WrapPromise.js` component. 

### WrapPromise()

This is the most critical part of this tutorial because it communicates with Suspense. `WrapPromise()` is more like an engine that wraps a promise and enables us to create a `Resource` variable. This special `Resource` variable will make rendering fetched data in our component a reality. I talked about  `Resource` in the next section but won't go further with it here because it will make less sense. `WrapPromise()` is likened to a wrapper, and this wrapper's job is simply to wrap a promise and instigate a method that tells if the fetched data is completed or ready to be read. 

`WrapPromise()`  takes in a Promise as an argument. The promise argument is either a network request that retrieves data from an [API](https://www.dropbox.com/scl/fi/orwvqe8j985ubmtpkvxdf/Data-fetching-in-React-using-Suspense.paper?dl=0&rlkey=483fmwb4c8lmfau630whfppbr#:uid=408513876318412308718295&h2=API) or a Promise object. When the promise is resolved, it returns the result, and if rejected, it throws an error. It uses the `read()` method to read the current status of the promise.

Let me work you through how this function is used. I will paste everything in the `wrapPromise.js` file, and below, I will break it down into understandable units afterward. 

```javascript
//WrapPromise.Js

const dataFetch = () => {
  const userPromise = fetchUser;
  return {
    user: wrapPromise(userPromise),
  };
};

const wrapPromise = (promise) => {
  let status = "pending";
  let result;
  let suspend = promise().then(
    (res) => {
      status = "success";
      result = res;
    },
    (err) => {
      status = "error";
      result = err;
    }
  );
  return {
    read() {
      if (status === "pending") {
        throw suspend;
      } else if (status === "error") {
        throw result;
      } else if (status === "success") {
        return result;
      }
    },
  };
};

export default dataFetch;
```

Before the wrapper function was created, we had to create a `dataFetch()` function that we could export to other components. In the `dataFetch()` function, we created a variable  `userPromise` which is returned from `fetchUser()`. What we aim to return from `dataFetch()` is an object from `User`. However, we are not returning the promise itself. Rather, we will return `wrapPromise()` with the promise. It will look like this: 

```javascript
const dataFetch = () => {
  const userPromise = fetchUser;
  return {
    user: wrapPromise(userPromise),
  };
};

export default dataFetch;
```

To progress, we created the `WrapPromise()` function. The  `WrapPromise()` function took in a promise as an argument. Below are the things done in the function.

- First, we need to set the initial `status`: by default, it is pending. 
- Secondly, we will want to store the `result`.

We create a variable called `suspend`, which is to be assigned to the promise passed in. We will call the `.then()` method. Inside this method, for the response:

- We want to set the `status` initialized above, which is `pending` by default, to `success`.
- Then, we set the result to whatever is passed in from `res`. If there is an error, we will set the `status` equal to `error` and the `result` equal to `err`.

```javascript
const wrapPromise = (promise) => {
  let status = "pending";
  let result;
  let suspend = promise().then(
    (res) => {
      status = "success";
      result = res;
    },
    (err) => {
      status = "error";
      result = err;
    }
  );
};
```

Now, regarding what we want to return in the `wrapPromise()` is a method called `read()`. In `read()`, we simply want to check the `status`:

- If the `status` is equal to `pending`, meaning it's still fetching the data, we will throw the `suspend`, which will be caught with the `.then()`.
- If there is an `error` (that is, `status` is equal to an `error`) we will throw the `results`. Remember that the result could have either the `data` or the `error`. In this case, it will have the `error`. 
- Finally, if the `status` is equal to `success`, which means everything went okay, we will simply return the `result` that will have the `data`.

```javascript    
{
  if (status === "pending") {
    throw suspend;
  } else if (status === "error") {
    throw result;
  } else if (status === "success") {
    return result;
  }
}
```


## Open Source Session Replay

_[OpenReplay](https://github.com/openreplay/openreplay) is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data._

![replayer.png](https://static.openreplay.com/images/banner-blog.png)

_Start enjoying your debugging experience - [start using OpenReplay for free](https://github.com/openreplay/openreplay)._



## Display Fetched data 

In this section, we have a component named `profile.js`. This component communicates with the `dataFetch()` function and stores the results in a special variable called `Resource`, clarified in the steps below.  The stored results are rendered in the component.

- We will import `dataFetch()` function into `profile.js` component.
- Then, declare a `resource` variable that calls the `dataFetch()` function from `wrapPromise.js` file. Having a `resource` variable is a common practice in the Suspense fetching method; it means the root of where the fetching was done.
- Inside the components, we will use the `Resource` variable to get the user's profile and declare the `read()` function, telling React to read and render the data being fetched.
- In the JSX we will render the user details and posts.
- In the main `App.js` component, we will wrap these components with Suspense, which carries a `fallback` spinner.

```javascript
//profile.js
import React from "react";
import dataFetch from "./Api";

const resource = dataFetch();

const UserProfile = () => {
  const user = resource.user.read();
  return (
    <div className="container">
      <h1 className="title">{user.name}</h1>
      <ul>
        <li>username:{user.username}</li>
        <li>phone:{user.phone}</li>
        <li>website:{user.website}</li>
        <li>email:{user.email}</li>
      </ul>
    </div>
  );
};

export default UserProfile;
```

## Using Suspense

In this section, we will import Suspense and `UserProfile` into our `App.js` component. We'll go ahead to wrap our profile component with Suspense. We also have a spinner to improve the client's user experience.

```javascript
import React, { Suspense } from "react";
import "./App.css";
import UserProfile from "./profile";
import Spinner from "./spinner";

function App() {
  return (
    <div className="App">
      <Suspense fallback={<Spinner />}>
        <UserProfile />
      </Suspense>
    </div>
  );
}
export default App;
```     

This is the result: when getting data, we get a spinner until the results come through.

![result](https://blog.openreplay.com/f617142a287b5338e25210e1cbe8283a/image01.gif)

## Conclusion

In this article, we looked at Suspense, its data fetching steps, and samples. It is undoubtedly the perfect user experience and asynchronous operation handler tool in React. 

React docs - [suspense](https://17.reactjs.org/docs/concurrent-mode-suspense.html).

[![newsletter](https://blog.openreplay.com/static/e6b18eda7a327511296a2f27692f9c70/e9957/newsletter.png)](https://newsletter.openreplay.com/)