Oh hi there, good day to you. My name is Sander, a passionate developer with a creative mind™.
Jump to content
Sander

˱Suspense˲

Publised on
5 min read
Unravelling the magic from React

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 componentarrow pointing to imagean animated gif showing a popular 'magic' meme


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 we need to create something we need to wait for. In this example we will 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 we need to have a method in which we can wrap our promise.

For this we can 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 we can use this inside of our 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 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 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.

Footnotes

  1. As of writing this article, Reacts use hook is still experimental and not available in the stable release.