Suspense was added in React version 16.6.0. It was introduced to declaratively specify the loading UI for asynchronous operations while the user is waiting.
In earlier versions of React, Suspense could be used for code splitting with React.lazy. But the React team envisions Suspense to handle asynchronous operations like loading code, data, images, etc as well.
With the ever-evolving nature of React, now is the right time to hop on this train and learn the basics of data fetching with Suspense. Whether you are at a beginner, intermediate or expert level in React, you will learn something new from this article.
Render-as-you-fetch has been the talk of the town as the latest data fetching pattern in React. It complements and leverages the Suspense capabilities to provide a smooth developer and user experience. We will create and understand a primitive useFetch hook, that implements this pattern.
Check out the demo application and the source code.
suspending
.suspender
.
Using Suspense gives us the capability to decide what should be shown until the asynchronous data is loaded. We don't need to do any explicit state management to control the visibility of the fallback UI. The onus of handling these intricacies is with React.
Since React’s inception, the React team and community have established various data fetching approaches. Each with its own pros and cons. Let’s summarize the major ones:
Approach 1: Fetch-on-render
In this approach, components trigger data fetching in their effects or lifecycle methods. Fetching starts only after the component has been rendered. This approach often leads to “waterfalls”. Read more about this pattern
Approach 2: Fetch-then-render
In this approach, first, all the data for the component is fetched. The component is rendered only after data is fetched and provided as a prop to the component. This approach does solve the “waterfall” problems but only to a limited extent. Read more about this pattern
Approach 3: Render-as-you-fetch
In this approach, the component that performs data fetching is wrapped in Suspense. A fallback UI prop is provided to Suspense. It is rendered until the data is fetched. To indicate that the fallback UI needs to be shown, the component must throw a promise. Once the promise is settled, React renders the actual component and removes the fallback UI. Read more about this pattern
In this article, we will focus on Approach 3.
The interaction between the promise thrown by the component and React Suspense is the heart of the render-as-you-fetch pattern. Let's create a hook to dig deeper.
/* Simple fetcher function that accepts a URL, makes a fetch request and returns data in JSON format */
const fetcher = async <T>(url: string): Promise<T> => {
const response = await fetch(url);
return response.json();
};
/* Cache to store fetch result, which can be a promise, error or data */
const cache = new Map();
/* Suspends the rendering by throwing a promise. Returns the result (data or error) when thrown promise settles */
export const useFetch = <T>(url: string): T => {
if (cache.has(url)) {
const { promise, data, error } = cache.get(url);
if (promise) throw promise;
if (error) throw error;
else return data;
}
/** aka suspender */
const promise = fetcher<T>(url)
.then((data) => cache.set(url, { data }))
.catch((error) => cache.set(url, { error }));
cache.set(url, { promise });
throw promise;
};
The building blocks of the above hook are:
The fetcher function accepts a URL, makes a fetch request and returns data in JSON format.
It can be extended further to:
Avoids component “waterfalls”.
Since React can render multiple data fetching components parallelly, there would be no waterfalls.
Early initiation of data fetching.
This pattern triggers data fetching as soon as the component starts rendering. This saves up on the time needed by the component to receive the data.
Makes code simple to read and understand.
Single responsibility of data fetching component.
React will extend Suspense functionality.
The useFetch hook built above demonstrates the crux of how render-as-you-fetch with Suspense works. This is the core of full-fledged data-fetching libraries like
The community has accepted Suspense to be a game-changer in the React ecosystem which will keep evolving. So get on this ride and fasten your seat belts. It is going to be an exciting journey ahead!
For a visual understanding of render-as-you-fetch, you can check out the
If you find this article useful, please let me know in the comments.
Featured image by Ant Rozetsky on Unsplash