The Suspense is Killing Redux

Ryan Florence
Oct 5, 2018 · 4 min read

At my latest workshops I’ve been getting this question:

So does Suspense kill Redux?

First, that’s a rude way to put it. But I get it, the suspense is killing you — or maybe Redux.

Washington DC Workshop at Viget, October 4th, 2018

React Suspense is all about handling transitions between views that have asynchronous data requirements — which redux doesn’t even attempt to handle. But, to make that work, Suspense is incidentally concerned with handling client-side data — which Redux is very interested in.

If you haven’t heard about React Suspense, give Dan Abramov’s JSConf Iceland talk a few minutes of your time.

Basic Suspense Setup

Here’s a cache:

Here’s a resource:

It takes a function that returns a promise and that’s it. Finally, here’s a component that uses the resource and the cache:

Behold, Suspense.

If we were to render this app, here’s a play-by-play:

  1. React starts rendering (in memory).
  2. It hits that call.
  3. The cache will be empty for the key (the id is the key) so it will call the function we provided to createResource, firing off the asynchronous fetch.
  4. AND THEN THE CACHE WILL THROW THE PROMISE WE RETURNED (Yeah, I’ve never thought about throwing anything but errors either, but you can throw window if you want.) After a throw, no more code is executed.
  5. React waits for the promise to resolve.
  6. The promise resolves.
  7. React tries to render Invoices again (in memory).
  8. It hits again.
  9. Data is in the cache this time so our data can be returned synchronously from
  10. React renders the page to the DOM

Pretty cool, yeah? The ability to treat our asynchronous data as synchronous function calls feels amazing when you start using it.

There’s more to say about handling placeholders and spinners for when data is loading, but we aren’t going to get into that here. It’s enough to say that React will leave the old screen on the page until the new data lands, or it will transition to placeholders if the data is taking longer than a user-specified amount of time.

Okay, So What About Redux?


  • store → cache
  • mapStateToProps →
  • action → function passed to resource

The reducer and dispatch just sort of disappear because we don’t reduce over actions anymore, we read from resources. When resources aren’t available yet, rendering suspends and waits for data to land. In a way, the cache + resources handle dispatch, reducers, and actions all at once.

Also note that in Suspense we no longer need the lifecycle hooks. When the Invoice is rendered with a different invoiceId the cache will be empty for that key, kicking off a new request automatically. This is significantly simpler than diffing props in lifecycle hooks and dispatching when they change.

In my experiments, there is a lot less to worry about when fetching data with Suspense, whether I'm coming from Redux or component state.

So, if your primary use-case for Redux is using it as a client-side cache of server-side data then Suspense could replace your redux usage. I’d consider it because you’d have simpler code and the ability to tame all those spinners.

What about cache invalidation?

Additionally, the way suspense handles the update feels great: the old page is still around and interactive while the new version is being rendered in memory. When the new data lands, the page updates with fresh data from the server. Don’t forget, that’s how plain ol’ server rendered apps work, too.

Finally, this approach makes middleware that synchronizes client and server data unnecessary.

It Doesn’t Replace Everything

However, in my experience, the majority of my clients and people I talk to are using Redux for little more than a client-side cache of server-side data. If that’s you, I think you’ll enjoy the simplicity and user experience Suspense provides.

Attend a Workshop

View Workshop Cities and Dates

Ryan Florence

Written by

Making React accessible for developers and their users at with OSS, Workshops and Online Courses. (Formerly at React Training)