Although still considered experimental, React Suspense is certainly worth trying, even if for smaller personal projects.
Suspense helps control component life cycles declaratively and prevent race conditions, among other things.
Using Suspense and hooks, we can write clean and functional code to manage state and handle errors while fetching GraphQL data.
Overview of React Suspense
One of the primary use cases of Suspense is managing the application state during async operations. With Suspense, you avoid writing annoying imperative code, like conditional rendering, and try/catch statements inside of your components.
Another well-known use case is code-splitting , but we won’t cover it in this post.
In a typical modern UI, there are tons of things to take care of when fetching data. You have to toggle loaders, prevent race conditions, and maintain the correct order of events. Suspense makes all of it infinitely simpler. Here’s how you would typically use React Suspense:
One interesting thing you might’ve noticed is that we create an
initialResource object right when the app loads. So what is this resource anyway? Let’s briefly cover resources, they are pretty important.
Resources are sources of async data for Suspense. Under the hood, a resource is simply an object that has a
read method. React Suspense will call that method as needed for its inner workings.
So what exactly does Suspense do for us?
Notice how we call
resource.user.read() without any safe checking. Getting user info is an async process, yet we’re using the resource synchronously because we know that Suspense will take care of fetching and checking the data. All we need to do is provide Suspense with a
This approach makes our application cleaner and more robust by eliminating the need for safe checking and conditional rendering and preventing race conditions.
Suspense with GraphQL
We’ll build a simple app that displays SpaceX rocket launches using their public
We’ll be using Apollo client , just because it’s much easier to work with in my experience. But you could do the same thing using any other GraphQL client.
Adding apollo client
First, let’s set up our Apollo client that we will use to send queries.
Adding graphql query
Now we will add the query for fetching launches. To follow good practices, we’ll store it in a separate file:
This is where Suspense comes into play. Let’s make a factory function that accepts a GraphQL query and returns a resource:
Our factory function tracks the status and the server response of the async request. We also have a
suspender that stores the GraphQL query promise. When we hear back from the server, we update the
So the factory function returns a resource, and, as I mentioned, a resource is simply an object with the
read method. The
read method does the following:
- If the status is pending,
suspenderpromise, which Suspense will catch and display the
- If the status is
readreturns the result.
- If the status is
throws theresult`, which, in that case, is an error instance.
Believe it or not, that was the most complex piece.
Strictly speaking, we could start using
wrapGraphql directly without wrapping it into a hook. But a custom hook is a nice abstraction and is handy if we decide to add things later, like checking session status or permissions before sending a request.
The code for
useAPI is straightforward. The hook accepts a query and uses the
wrapGraphql factory function to return a new resource.
Finally, here’s how we would use it:
As a side note: we used Apollo for the GraphQL client, however, another alternative to consider is Relay . Relay is closely integrated with React Suspense and could be a better choice for you. With that said, it’s also much more opinionated, and I find Apollo easier to use.
That’s it for this post. We used React Suspense with GraphQL, but you can certainly adapt it to work with any other async source. Suspense also has more applications than just handling async operations. The best place to begin your learning is the official Suspense overview doc.
Here’s a link to the codesandbox for this tutorial.