˱Suspense˲
While trying to answer this question on StackOverflow, I realized that I didn’t know enough about the workings of Suspense myself to give a good answer. So I decided to do some research and write this article.
<Suspense>
Suspense
lets you display a fallback until its children have finished loading react documentation
<Suspense fallback={<Loading />}>
<SomeComponent />
</Suspense>
But how does it work?
Neat, but how does it actually works? How does Suspense
know when to show the fallback and when to show the child component?
When I asked around I just got the answer that “it’s magic”.
But I don’t like magic code, so let’s find out how it works
TL;DR
Suspense
acts as a fancy try/catch
block.
Suspense
will try
to render your component and whenever a Promise
is being thrown it will catch
it and render the fallback component until the promise resolves.
<Suspense
fallback={
{
/* Catch */
}
}
>
{/* Try */}
</Suspense>
How people perceive the Suspense component
Hey you made it this far!
This means you are interested in how things works instead of accepting the magic, have a
As seen in the TL;DR - whenever something throws a promise from within the Suspense
component, it will render the fallback component until the promise resolves.
function ComponentWhichThrows() {
throw new Promise(() => {}); // This will trigger the fallback state
return "hi there";
}
export default function App() {
return (
<div className="App">
<Suspense fallback={<div>loading....</div>}>
<ComponentWhichThrows />
</Suspense>
</div>
);
}
Simple as that, A fancy try/catch
for React.
Let’s see if we can make it a bit more interesting and make an async call using suspense.
First you’ll need to create something you’ll need to wait for. In this example you can create some fake data fetching function as this is a common use case. In reality, Suspense
can work with any asynchronous operation.
export function fakeApi(name) {
return new Promise((resolve) => {
setTimeout(() => {
resolve("Hello " + name);
}, 2000);
});
}
To play nice with Suspense
you need to have a method in which you can wrap our promise.
For this, create a poor-mans implementation of Reacts use hook1.
// This is a poor-mans implementation of Reacts use hook
// please don't use this in production
export function use(promise) {
if (promise.status === "fulfilled") {
return promise.value;
} else if (promise.status === "rejected") {
throw promise.reason;
} else if (promise.status === "pending") {
throw promise;
} else {
promise.status = "pending";
promise.then(
(result) => {
promise.status = "fulfilled";
promise.value = result;
},
(reason) => {
promise.status = "rejected";
promise.reason = reason;
}
);
throw promise;
}
}
And finally use this inside of your component
function YourComponent() {
const data = use(fakeApi("xiduzo"));
return <div>{data}</div>;
}
export default function App() {
return (
<div className="App">
<Suspense fallback={<div>loading user</div>}>
<YourComponent />
</Suspense>
</div>
);
}
See this codesandbox for a working example.
Why would you use <Suspense>
<Suspense>
Suspense is a great way to handle asynchronous data fetching. It is a great alternative to the traditional way of handling data fetching in React.
More than lazy loading
Suspense was initially added to React to support lazy loading of components. But currently can also be used to await data fetching and adding of error boundaries.
User Experience (UX)
In order to create a better UX it is important to show the user something while we are waiting for data to load. It is also possible to nest Suspense
components for a more fine-grained experience.
Be a step ahead
Even though Suspense
is still experimental, libraries like Relay, SWR or @tanstack/react-query have added support for it.
Why wouldn’t you use Suspense
Suspense
Suspense
adds another layer of abstraction to your code. This can make it harder to understand what is going on. If your team is already comfortable with existing patterns for data fetching, the introduction of Suspense might require additional training and adjustment.
More than lazy loading
For simple applications with straightforward data fetching requirements, using Suspense might introduce unnecessary complexity. In such cases, simpler solutions like data fetching libraries might be more suitable.
User Experience (UX)
In scenarios where finer control over loading states or error handling is crucial, a more manual approach using traditional patterns like useEffect
and useState
might be preferred
Be a step ahead
The whole concept of Suspense
might also get overshadowed by server components in the future. Where the documentation mentions: When fetching data in a Server Component, prefer async
and await
over use
.
Conclusion
Suspense
can be a powerful tool in your toolbox, especially if you truly understand how it functions. Don’t treat it as your new shiny hammer and start hitting everything with it. Use it wisely and it will be a great addition to your work.
Unmentioned resources
Here are some other resources I found while researching Suspense
which might be interesting to you.
- Reacts codesandbox example
- React 17 documentation on suspense
- Another stackoverflow question
- And another stackoverflow question
- Logrocket blog post
Footnotes
As of writing this article, Reacts
use
hook is still experimental and not available in the stable release. ↩