How to Fetch Data with React Suspense
Tags: react, javascript
Published: 2019-12-16 10:05:49

_Read the original article [here](https://rahmanfadhil.com/fetch-data-with-react-suspense/)_

A while ago, React introduces a new set of features called "Concurrent Mode". And its basically allows you to interrupt the rendering process of your React components. And, with this feature comes the React Suspense.

Traditionally, data fetching in React looks like this:

```jsx
import React from "react"

class Example extends React.Component {
  componentDidMount() {
    const data = fetchSomething() // Fetch data here
  }

  // ...
}
```

It's very obvious that the request happened whenever this component is being rendered or mounted. Suspense allows us to "wait" until we get the data that being fetched asynchronously before we render our component.

Even though they are not yet available in the stable release, we can still enjoy these features by using the experimental builds.

## Getting Started

First, you need to have a React application. If you don't have one already, you can easily use [create-react-app](https://facebook.github.io/create-react-app) by running this command.

```
$ npx create-react-app learn-suspense
```

Once your project has been created, we need to install the experimental version of our `react` and `react-dom` package.

```
$ npm install react@experimental react-dom@experimental
```

I also have published the source code of this entire project on my GitHub. Go ahead and clone it into your computer.

```
$ git clone https://github.com/rahmanfadhil/learn-suspense.git
```

## Enable concurrent mode

Before we get started using Suspense, we need to enable the concurrent mode first. We can do that by using the `createRoot` method to render our `App` component in the `index.js` file.

```jsx
import React from "react"
import ReactDOM from "react-dom"
import App from "./App"

const element = document.getElementById("root")
ReactDOM.createRoot(element).render(<App />)
```

## Fetch data

Now, let's create file called `resource.js`.

Here, we're going to write a new function to make an HTTP request. In this example, we're going to use [JSONPlaceholder](https://jsonplaceholder.typicode.com). An easy-to-use, open-source, fake REST API for testing front-end applications.

```js
async function fetchPosts() {
  const response = await fetch("https://jsonplaceholder.typicode.com/posts")
  const data = await response.json()
  return data
}
```

Here, we're using the JavaScript [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to fetch some fake blog posts, and return the result.

We also need to create a function called `createResource`.

```js
// ...

export default function createResource() {
  let status = "loading"
  let result
  let suspender = fetchPosts().then(
    data => {
      status = "success"
      result = data
    },
    error => {
      status = "error"
      result = error
    }
  )

  return {
    read() {
      if (status === "loading") {
        throw suspender
      } else if (status === "error") {
        throw result
      } else if (status === "success") {
        return result
      }
    }
  }
}
```

We will use this function to display the blog posts in our React components. This allows us to tell React that we want to "wait" for our component gets rendered until we have done fetching data from the server.

So, when we execute this function, it will run the `fetchPosts` function where we fetch our blog posts. Then, whenever the promise is completed or rejected, we change the status of the `status` variable.

The `read` method will be used to get the data which will be displayed into the browser. When the promise is still pending, we need to throw the `suspender` variable, which contains our promise. If it gets rejected, we throw our error that lives inside the `suspender` variable. Finally, we just return the result when the data has fetched successfully.

## Display fetched data

Now, let's create a component that will display our blog posts. We'll call it `PostList` component. Let's put it inside `PostList.js` file.

```jsx
import React from "react"

export default function PostList({ resource }) {
  const posts = resource.read()

  return (
    <ul>
      {posts.map((post, i) => (
        <li key={i}>{post.title}</li>
      ))}
    </ul>
  )
}
```

Here, we are accepting a `resource` props which contains our resource. Then, we display an unordered list and loop through the blog posts that we have just fetched.

Now in our `App.js`, we can use the `PostList` component.

```jsx
import React, { Suspense } from "react"
import PostList from "./PostList"
import createResource from "./resource"

const resource = createResource()

export default function App() {
  return (
    <div>
      <h1>Blog Posts</h1>
      <Suspense fallback={<h1>Loading...</h1>}>
        <PostList resource={resource} />
      </Suspense>
    </div>
  )
}
```

Here, we're fetching the blog posts by executing the `createResource` function. Then, we render our `PostList` component and wrap it inside the `Suspense` component. This component takes a `fallback` prop, where we display the loading component.

We also need pass the resource into our `PostList` component, so that it can display the result.

Cool! we can finally test our app to make sure everything is running as expected.

![loading](https://thepracticaldev.s3.amazonaws.com/i/2e8zzof8r9gwr821yzzq.png)

![loaded](https://thepracticaldev.s3.amazonaws.com/i/39renb9jqs7uwaxiee8q.png)

## Fetch multiple data

There are a lot of cases where you want to fetch multiple data at once. But for now, we need to rewrite the `createResource` function over and over again to fetch a different endpoint or server.

We can prevent this by extracting the `createResource` function into something like this.

```js
// ...

function wrapPromise(promise) {
  let status = "loading"
  let result
  let suspender = promise.then(
    data => {
      status = "success"
      result = data
    },
    error => {
      status = "error"
      result = error
    }
  )

  return {
    read() {
      if (status === "loading") {
        throw suspender
      } else if (status === "error") {
        throw result
      } else if (status === "success") {
        return result
      }
    }
  }
}

export default function createResource() {
  return {
    posts: wrapPromise(fetchPosts())
    // add here
  }
}
```

Now, with this approach, you can add more data into your resource by simply wrap your promises with `wrapPromise` function.

We also need to do some adjustment into your `PostList` component.

```jsx
import React from "react"

function PostList({ resource }) {
  const posts = resource.posts.read() // change this

  return (
    <ul>
      {posts.map((post, i) => (
        <li key={i}>{post.title}</li>
      ))}
    </ul>
  )
}
```

Cool, so that's basically how you can fetch data with React suspense. If you don't want to implement this code by yourself, especially the `resource` and `wrapPromise` part, you can use this awesome library called [SWR](https://github.com/zeit/swr). It offers some additional features like loading state, error catching, and custom fetcher.