React Suspense for Data Fetching with Axios in React 18
Tags: webdev, javascript, react, programming
Published: 2023-05-08 23:16:48

In this blog post, we will what React Suspense is and how it can be used for Data Fetching in React 18.

React Suspense has been in the making for a long time, but it has now been released as a stable feature part of [Concurrent React](https://react.dev/blog/2022/03/29/react-v18#what-is-concurrent-react) and makes use of the new Concurrent rendering engine released in React 18.

Suspense allows displaying a fallback component until the child component has finished loading the data.

Let's build an application that uses Suspense along with Axios to fetch data from the jsonplaceholder API to display a list of Posts.

**Dead Simple Chat allows you to add chat in your React Applications using powerful** [**JavaScript Chat API and SDK**](https://deadsimplechat.com/javascript-chat-api-sdk)**. With Dead Simple Chat you can add chat to your application in minutes.**

## Step 1: Scaffold your React Application

We will use `create-react-app` to scaffold our application, open the terminal and `cd` into the directory where you want to scaffold the application and run the following command:

```bash
npx create-react-app react-suspense-demo
cd react-suspense-demo
```

## Step 2: Install Axios

Next, to use Axios in our application, we will install the Axios package using npm install.

```js
npm install --save axios
```

##   
Step 3: Create a custom hook for Suspense to work

We will first create a utility method to wrap the Axios request to work with React Suspense.

Create a file under the `src/` folder called as `useGetData.js` that will contain the code for our custom hook.

```js
// src/useGetData.js
import { useState, useEffect } from "react";
import axios from "axios";

const promiseWrapper = (promise) => {
  let status = "pending";
  let result;

  const s = promise.then(
    (value) => {
      status = "success";
      result = value;
    },
    (error) => {
      status = "error";
      result = error;
    }
  );

  return () => {
    switch (status) {
      case "pending":
        throw s;
      case "success":
        return result;
      case "error":
        throw result;
      default:
        throw new Error("Unknown status");
    }
  };
};

function useGetData(url) {
  const [resource, setResource] = useState(null);

  useEffect(() => {
    const getData = async () => {
      const promise = axios.get(url).then((response) => response.data);
      setResource(promiseWrapper(promise));
    };

    getData();
  }, [url]);

  return resource;
}

export default useGetData;
```

We created a utility method called the `promiseWrapper` to wrap the Axios request, and then created a custom hook called as `useGetData` that we will use in our components to send HTTP Get requests using Axios.

The hook uses a state variable called as the `resource` to store resources we get after wrapping our Axios promise around our `promiseWrapper` method.

Our `promiseWrapper` method returns a function when called it runs the switch case on the current state of the promise and returns accordingly:

* `pending` -  If the status of the promise is `pending` then it returns the promise itself. This causes the React Suspense to trigger the fallback component.
    
* `success`**\-** If the status of the promise if `success` then it returns the value returned after resolving the promise.
    
* `error` - If the status of the promise is `error` then it throws an error
    

## Step 4: Creating PostsComponet to display a list of posts

Now we will create a `PostsComponet` that will use our custom hook, and call the [`jsonplaceholder.typicode.com/posts`](http://jsonplaceholder.typicode.com/posts) API to fetch a list of mock posts.

```js
// src/PostsComponent.js
import React from "react";
import useGetData from "./useGetData";

function PostsComponent() {
  const data = useGetData("https://jsonplaceholder.typicode.com/posts");

  return (
    <div>
      {data &&
        data.map((post) => (
          <div key={post.id}>
            <h2>{post.title}</h2>
            <hr />
            <p>{post.body}</p>
          </div>
        ))}
    </div>
  );
}

export default PostsComponent;
```

This component is very simple, we are calling our custom hook `useGetData` and giving it an endpoint URL to fetch the list of posts.

Assing the result from the custom hook to a variable called as `data` and displaying it on the screen.

## Step 5: Wrapping our PostsComponent in Suspense

Finally, it's time to use our PostsComponent in our application open `App.js` and add the following code to the file:

```js
// src/App.js
import React, { Suspense } from "react";
import PostsComponent from "./PostsComponent";

function App() {
  return (
    <div className="App">
      <Suspense fallback={<div>Loading Posts...</div>}>
        <PostsComponent />
      </Suspense>
    </div>
  );
}

export default App;
```

We are adding the `<PostsComponent />` as a child to the `<Suspense fallback={}></Suspense>` component.

The Suspense component has a `fallback=` prop, here you can pass a component that you want to display until the child component loads.

In our example, we are showing just basic div which text "Loading Posts..."  

You can see the [working example here](https://deadsimplechat.com/blog/react-suspense/#step-5-wrapping-our-postscomponent-in-suspense)

## Step 6: That's it

We have seen how to use `Suspense` for data fetching in React 18, hopefully, the above example was helpful in understanding how to use the `Suspense` component.

## Adding Delay in Loading Post

To show clearly that our Suspense is working, we can add a `setTimeout` in our `promiseWrapper`  

```js
const promiseWrapper = (promise, delay = 3000) => {
  let status = "pending";
  let result;

  const s = promise
    .then((value) => {
      return new Promise((resolve) => {
        setTimeout(() => {
          status = "success";
          result = value;
          resolve(value);
        }, delay);
      });
    })
    .catch((error) => {
      status = "error";
      result = error;
    });

  return () => {
    switch (status) {
      case "pending":
        throw s;
      case "success":
        return result;
      case "error":
        throw result;
      default:
        throw new Error("Unknown status");
    }
  };
};
```

And we will also update our `App.js` file and added `LoadingScreen` component to display a loading message.

```js
// src/App.js
import React, { Suspense } from "react";
import PostsComponent from "./PostsComponent";

function LoadingScren() {
  return (
    <div>
      <h1>Loading Posts</h1>
      <h3>loading amazing posts for you to read</h3>
    </div>
  );
}

function App() {
  return (
    <div className="App">
      <Suspense fallback={<LoadingScren />}>
        <PostsComponent />
      </Suspense>
    </div>
  );
}

export default App;
```

Here is the final result:

you can see the [working example here](https://deadsimplechat.com/blog/react-suspense/#comparison-without-using-suspense)

## Comparison without using Suspense

Let's also see how our code would have looked if we had not used React Suspense.

We would have used a combination of `useEffect` and `useState` to achieve similar results.

Our code `PostComponent.js` would look like this:

```js
// src/PostsComponent.js

import React, { useEffect, useState } from "react";
import axios from "axios";

function PostsComponent() {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  useEffect(() => {
    async function fetchPosts() {
      const response = await axios.get(
        "https://jsonplaceholder.typicode.com/posts"
      );
      setIsLoading(false);
      setData(response.data);
    }
    fetchPosts();
  }, []);

  if (isLoading) {
    return <div>Loading Posts...</div>;
  }

  return (
    <div>
      {data &&
        data.map((post) => (
          <div key={post.id}>
            <h2>{post.title}</h2>
            <hr />
            <p>{post.body}</p>
          </div>
        ))}
    </div>
  );
}

export default PostsComponent;
```

We have used the `useEffect` hook to make the API call to fetch the list of posts and set the result in a state variable that we will display.

To handle the loading state, we have created a state variable called as `isLoading` and we are setting it to `true` by default, once the posts are loaded we are updating the `isLoading` state variable to `false`.

**Using** [**JavaScript Chat SDK**](https://deadsimplechat.com/javascript-chat-api-sdk)**, integrate in-app chat in your React application in minutes. Dead Simple Chat is a highly scaleable chat solution that can be used for any chat use case.**

## Handing Errors in Suspense with Error Boundaries

React Suspense has support for `ErrorBoundaries`, we can wrap the `Suspense` component in an `ErrorBoundary` and the error thrown by the Suspense's Child Component will be gracefully handled by the `ErrorBoundary` component.

Let's update our example to use `ErrorBoundary`.

Create a file called as `src/ErrorBoundary.js` to hold our `ErrorBoundary` component:

```js
import { Component } from "react";

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // Report the Error to Some Error Reporting Service.
    console.error("Error:", error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback;
    }

    return this.props.children;
  }
}

export default ErrorBoundary;
```

The `ErrorBoundary` can only be a class component, the other components in our application can be as-is functional components, but just the ErrorBoundary component has to be the class component because the lifecycle methods, `componentDidCatch` and `getDerviedStateFromError` are not available in functional components.

Our `ErrorBoundary` component accepts a `fallback` prop which is returned when an error is detected.

We can pass a component to the `fallback` prop and that component will be displayed in case of an error.

Finally, we will update our `App.js` code to use our `ErrorBoundary` component:

```js
// App.js
import React, { Suspense } from "react";
import ErrorBoundary from "./ErrorBoundary";
import PostsComponent from "./PostsComponent";

function LoadingScren() {
  return (
    <div>
      <h1>Loading Posts</h1>
      <h3>loading amazing posts for you to read</h3>
    </div>
  );
}

function App() {
  return (
    <div className="App">
      <ErrorBoundary fallback={<div>Error Occurred when loading Post..</div>}>
        <Suspense fallback={<LoadingScren />}>
          <PostsComponent />
        </Suspense>
      </ErrorBoundary>
    </div>
  );
}

export default App;
```

That's it! Now when a network error occurs when loading the posts, our ErrorBoundary will handle it and display our fallback component which is a div tag with a message.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mnfhj5ecfz9nv63barbl.png)

## Conclusion

Hopefully, this post was helpful in understanding the concept of React Suspense. We have looked at an example of how to use React Suspense with Axios to fetch data.

We also looked at the example of how we would have done data fetching without using Suspense and looked at an example of error handling using Suspense.

this article was originally written on DeadSimpleChat website: [React Suspense for Data Fetching with Axios in React 18](https://deadsimplechat.com/blog/react-suspense/)