ReactJS - <Suspense> Component



In React 18, the new feature <Suspense> was introduced, which allows our component to "wait" on anything before rendering. So we cover the fundamentals of Suspense in this tutorial.

Suspense is the first feature that allows us to create apps with more responsive user interfaces that use less browser resources. It also gives a more user-friendly API for developers and designers to work with.

Suspense strongly alters how React selects what to render on a web page based on changes in our app's component state. If the data in our React app (state data) changes, and the changes do not require a full page reload from the server, the React rendering engine updates the UI.

When a web page has to refresh a list based on anything like typing in a search box, there is a significant difference between how React used to function and how it works with Concurrent Rendering.

Previously, if we typed something into the search box, React might update many things in the list at once. This can cause the user to view the web page as slow and unresponsive. It also forced our computer to work overtime to keep the page functioning smoothly.

React is stronger now, because of Concurrent Rendering. It is capable of managing these changes more efficiently. As a result, when we type in the search box, React can ensure that the list refreshes in some way.

Syntax

<Suspense fallback={<Loading />}>
   <MyComponent />
</Suspense>

Props

  • children − This is what we want to show on our webpage - like text, images, or anything else we want to display. Or we can say it is our main content that we want to display.

  • fallback − If the thing we want to show (children) is not ready yet, we can have a backup plan. This backup plan is called the "fallback." It's usually something simple, like a loading spinner or a basic placeholder.

How to use it?

Suspension is a new feature that allows our component to "wait" for something before rendering. It is used for data fetching and waiting for images, scripts, and other asynchronous operations to load, among other things.

Suspense does not detect data retrieval within an Effect or Event handler. Only the Suspense-enabled data source will activate the <Suspense> component.

Examples

So we will see some examples and usage of the <Suspense> feature using some small applications of React.

Example − Loading Placeholder

We can place a specific box around a section of our website and tell the browser, "If anything inside this box is taking a long time to load, show this loading message instead."

Assume we want to display a "Loading..." notification while a list of photos is being fetched. We can do it this way −

<Suspense fallback={<LoadingMessage />}>
   <PhotosComponent />
</Suspense>

So, this way we can keep our website looking nice and welcoming by showing a loading message when the browser takes a long time to load and then showing the real content when it is ready.

Now let's create a simple app that fetches a list of items from an API

First, we need to set up our React project with Create React App. In the src folder, create two components: ItemList.js and LoadingMessage.js. In LoadingMessage.js we will define a simple loading message component −

import React from "react";
function LoadingMessage() {
   return <div>Loading...</div>;
}
export default LoadingMessage;

In the ItemList.js we will create the component that fetches and displays the list of items using the fetch function. You can also use the Suspense component to handle loading −

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

const fetchItems = () =>
new Promise((resolve) => {
   setTimeout(() => {
      resolve(["Item 1", "Item 2", "Item 3"]);
   }, 2000);
});

function ItemList() {
   const [items, setItems] = useState(null);   
   useEffect(() => {
      const fetchData = async () => {
         const data = await fetchItems();
         setItems(data);
      };
      fetchData();
   }, []);
   
   return (
      <div>
         <h1>Items</h1>
         <ul>
            {items && items.map((item, index) => (
               <li key={index}>{item}</li>
            ))}
         </ul>
      </div>
   );
}

export default ItemList;

In our src/App.js we will use the Suspense component to wrap the ItemList component and provide the fallback −

import React, { Suspense } from "react";
import ItemList from "./ItemList";
import LoadingMessage from "./LoadingMessage";

function App() {
   return (
      <div className="App">
         <h1>My App</h1>
         <Suspense fallback={<LoadingMessage />}>
            <ItemList />
         </Suspense>
      </div>
   );
}

export default App;

Output

items

Example − Progressive Content Display

When a component suspends, the fallback is displayed by the nearest parent Suspense component. We can nest multiple Suspense components together to create a loading pattern. As the next level of content becomes accessible, the fallback for each Suspense border will be completed.

Let's say we are creating a webpage with two sections: a News Feed and a Weather Widget, each with its own loading animations. So this is how we can use multiple Suspense components to create a loading sequence −

import React, { Suspense } from "react";

function NewsFeedSpinner() {
   return <div>Loading News Feed...</div>;
}

function WeatherWidgetSpinner() {
   return <div>Loading Weather Widget...</div>;
}

function NewsFeed() {
   // Loading news feed data...
   return <div>News Feed Content</div>;
}

function WeatherWidget() {
   // Loading weather data...
   return <div>Weather Widget Content</div>;
}

function App() {
   return (
      <Suspense fallback={<NewsFeedSpinner />}>
         <NewsFeed />
         <Suspense fallback={<WeatherWidgetSpinner />}>
            <WeatherWidget />
         </Suspense>
      </Suspense>
   );
}

export default App;

Output

news feed

Example − Handling Errors and Client-Only Content Fallbacks

When we use streaming server rendering in React, React continues rendering if a component generates an issue on the server. Instead, it locates the nearest <Suspense> component and displays its loading spinner on the page. The user will see a spinner while the website loads this way.

React tries to render the same component again on the client side. If there is still a problem, React displays it. However, if there is no error on the client side, the error is not sent to the user because the content eventually loads successfully.

We can use this to prevent some components from being rendered on the server. Throw an error in the server environment, then wrap it in a <Suspense> boundary to replace the HTML with fallbacks −

import React, { Suspense } from "react";

function Loading() {
   return <div>Loading Chat...</div>;
}

function Chat() {
   if (typeof window === "undefined") {
      throw new Error("Chat should only render on the client.");
   }
   // The Chat component logic goes here...   
   return <div>Chat Component (Client Only)</div>;
}

export default function App() {
   return (
      <div>
         <h1>Chat App</h1>
         <Suspense fallback={<Loading />}>
            <Chat />
         </Suspense>
      </div>
   );
}

Output

chatapp

In the application, we used a typeof window === "undefined" to check if the component is executing on the server side, and if it is, we throw an error to show that the "Chat" component should only render on the client side.

Warnings

  • If something in our app takes too long to load and becomes stuck, React forgets what it was doing. When it's finished, React simply restarts from the beginning.

  • If our app displayed something while loading but then became stuck, it would display the loading information again unless we used a specific trick called "startTransition" or "useDeferredValue."

  • When our app needs to conceal something that is currently on the screen because it has become stuck again, React will clear up some items to avoid slowing things down. When it is finished, React will show everything again and do some more cleanup.

reactjs_reference_api.htm
Advertisements