DEV Community

Abhay Singh Kathayat
Abhay Singh Kathayat

Posted on

Mastering React Suspense for Data Fetching: A Complete Guide

React Suspense for Data Fetching

React Suspense is a powerful feature that allows developers to manage asynchronous data fetching and code loading in a declarative way. Introduced as an experimental feature in React 16.6, Suspense enables developers to pause rendering of a component tree while waiting for some asynchronous task (such as data fetching) to complete. Once the task is finished, React can resume rendering the component and the UI updates automatically.

Suspense is particularly useful when combined with libraries like React Query, Relay, or Apollo Client, but it is a generic mechanism for handling asynchronous tasks within the React component tree.


How React Suspense Works

React Suspense works by wrapping a part of the component tree with the <Suspense> component and providing a fallback UI (usually a loading spinner or message). When React encounters an asynchronous task, it pauses the rendering of that component until the task is completed, and then renders the data.

Basic Syntax:

<Suspense fallback={<div>Loading...</div>}>
  <MyComponent />
</Suspense>
Enter fullscreen mode Exit fullscreen mode
  • fallback: This is the UI that will be displayed while the asynchronous task is pending. Once the data is ready, the actual component will be rendered.
  • MyComponent: This component can contain data fetching or other asynchronous operations.

Data Fetching with Suspense

In React, Suspense is commonly used for handling asynchronous operations, such as fetching data from an API. React will suspend rendering while it waits for the data to arrive and will show the fallback UI until the data is ready.

While React Suspense is not fully integrated with data fetching as of now (for general use cases), libraries like React Query, Relay, and Apollo Client already provide integrations with Suspense for better handling of data fetching.

Let's go through a basic example of using Suspense for data fetching using a custom fetchData function.


Example: Using Suspense for Data Fetching

1. Creating a Data Fetching Function

To enable Suspense for data fetching, we need to create a function that "throws" a promise when the data is not available, and React can catch it and suspend rendering.

let cache = {};

const fetchData = async (url) => {
  if (cache[url]) {
    return cache[url];
  }

  const response = await fetch(url);
  const data = await response.json();

  // Save the data in cache to avoid fetching again
  cache[url] = data;

  return data;
};

// This function wraps data fetching to throw a promise to enable Suspense
const createResource = (url) => {
  let status = "pending";
  let result;
  let promise = fetchData(url)
    .then(
      (res) => {
        status = "success";
        result = res;
      },
      (err) => {
        status = "error";
        result = err;
      }
    );

  return {
    read() {
      if (status === "pending") {
        throw promise; // This will make React Suspense wait
      } else if (status === "error") {
        throw result; // Handle error state
      }
      return result; // Return the fetched data
    },
  };
};
Enter fullscreen mode Exit fullscreen mode

2. Using Suspense with Data Fetching

Now, let's use the createResource function inside a component wrapped in a Suspense boundary. The fallback prop will display the loading state until the data is ready.

import React, { Suspense } from "react";

const resource = createResource("https://jsonplaceholder.typicode.com/posts");

const Posts = () => {
  const posts = resource.read(); // This will either throw a promise or return data
  return (
    <div>
      <h2>Posts</h2>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
};

const App = () => {
  return (
    <Suspense fallback={<div>Loading posts...</div>}>
      <Posts />
    </Suspense>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Explanation of the Code:

  1. createResource function: This function simulates the Suspense behavior by "throwing" the promise when the data is still being fetched. Once the data is available, it returns it to the component.
  2. Posts component: This component calls resource.read() to fetch the posts. While the data is being fetched, React Suspense suspends rendering, showing the fallback UI.
  3. App component: The Suspense wrapper is used to manage the loading state. The fallback prop specifies the UI to show while data is loading (in this case, "Loading posts...").

Handling Errors in Suspense

React Suspense has an error boundary mechanism, so when something goes wrong (e.g., the data fetch fails), React will catch the error and you can define an error boundary component to display the error message.

To handle errors, you can use the ErrorBoundary component. Here's how to add it:

import React, { Suspense } from 'react';

class ErrorBoundary extends React.Component {
  state = { hasError: false };

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

  componentDidCatch(error, errorInfo) {
    console.log("Error:", error);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

const App = () => {
  return (
    <ErrorBoundary>
      <Suspense fallback={<div>Loading...</div>}>
        <Posts />
      </Suspense>
    </ErrorBoundary>
  );
};
Enter fullscreen mode Exit fullscreen mode

In this example, the ErrorBoundary will catch errors and display an error message if anything goes wrong during the data fetching.


Benefits of Using Suspense for Data Fetching

  • Declarative Loading: Suspense lets you declaratively define what should happen while waiting for asynchronous tasks to complete, improving the readability of your code.

  • Automatic Re-rendering: React automatically re-renders the components once the data has been fetched, without you needing to manually update the state.

  • Simplifies Async Handling: Suspense allows you to handle asynchronous behavior without needing complex state management or lifecycle methods.

  • Works well with other async libraries: Libraries like React Query, Apollo Client, and Relay integrate with Suspense, providing automatic data fetching and caching mechanisms for a seamless experience.


Conclusion

React Suspense is a powerful tool for managing asynchronous data fetching in a more declarative way. While the feature is still evolving and its integration with data fetching is experimental in React, it already offers a better approach to managing loading states, data fetching, and error handling in your React components.

By using Suspense with data fetching, you can build more efficient and cleaner applications with less boilerplate code. However, for full-fledged apps, it’s recommended to use it with libraries such as React Query or Apollo Client for managing data and caching effectively.


Top comments (0)