The React team has published several in-depth posts about React Suspense such as this one. It took me a lot of effort to grok from them what are Suspense’s benefits and how to use it.
Now that I understand Suspense, here’s the image I wished I had seen when learning about it. I hope it’ll be useful to you:
So: a lot less boilerplate and the loading indicator clearly separated from the component.
FAQ
Why isn’t fetchData
awaited in the After example?
As you can see in the After code, fetchData
needs to be asynchronous (so it can call fetch
to get the data via HTTP for example), however, it is not awaited, nor returns a promise, and the code still works. How come?
Suspense expects components to respect this contract :
-
When called and they’re not ready, they throw the pending promise (yes, they throw the promise, not an
Error
, in the same way you could throw an integer or an array). -
When called and the data is ready, they return the element (and not a promise)
Therefore, fetchData
either throws a promise or returns the data, it never returns a promise and doesn’t need to be awaited.
For more details, you can check out this blog post, that explains algebraic effects and how they’re used in React (the “throw a pending promise” thing is a simulation of algebraic effects).
That’s the most complicated part of Suspense, which is intended to be implemented by library authors1. However, there are a few straightforward ways to implement it, such as implementing it in a caching layer.
All this to remove some boilerplate? I can do this with a HOC.
No, Suspense does much more:
-
In a traditional react app, the loading indicators are coupled to the data fetching. For example, if each component loads its data, one loading indicator per component will be displayed, and the components will load in a random order, as each component’s data finishes fetching.
With suspense, by positioning the
<Suspense>
components in the right place and nesting them, you can control how the loading indicators are displayed. For example, you can choose to only have one large spinner, or display the large spinner until the important components are loaded, then a small spinner for the non-important, slow-to-load components. -
Suspense integrates with the React Concurrent Mode. This brings even better stuff :
-
When loading several components one on top of the other (such as a header, then a first content, then a second content), you can force them to appear from the top to the bottom, even if the bottom one is ready first, and avoid ugly reflows
-
You can display the loading indicator only if the loading is slow, and otherwise transition directly from the previous to the next page of the single page app, without a loading indicator or a blank page
-
If two components finish loading almost simultaneously, you can display them simultaneously and avoid reflows
-
Great, can I use it?
Both Suspense and Concurrent Mode are experimental features, but they’re used in production at Facebook. The development experience and results are great, so I encourage you to use them.