Learn fundamentals of React Suspense

Kent C. Dodds
InstructorKent C. Dodds
Share this video with your friends

Social Share Links

Send Tweet
Published 5 years ago
Updated 3 years ago

Many components in your application will need data that has to be fetched over the network. When that data is loaded, however, you don't want to make a network request because you have the data stored in cache in the browser.

In this lesson, you will learn

  • throw a Promise
  • catch that Promise with Suspense
  • render that data to the browser after its loaded
  • add that data to cache

Kent C. Dodds: [00:00] Let's learn the fundamentals of React Suspense. The code that I'm about to write is definitely not a code that you're going to write in your typical code base, but it's how the abstractions that you're going to be using with Suspense work under the hood.

[00:12] Here we have a simple Pokémon app that will fetch us data about a particular Pokémon. If we put Mew in here, then it should fetch us data about that Pokémon that we can render here with some JSON.

[00:23] To fetch this data, we have this fetchPokemon API that we can call. That's a graphql API. All we need to do is pass it a name, and it will get us that Pokémon's ID, number, name, and attacks.

[00:36] The first thing that we're going to do is we need to bring in Suspense, so we have something that can handle the asynchrony. Then here, I'm going to render this PokemonInfo inside of Suspense. In the Suspense, we'll say, "Fallback is a div that says, 'Loading.'" Next, let's go ahead and create a cache. That's just going to be an object that will keep track of all of the Pokémon that we've loaded.

[01:04] Then I'm going to get the Pokémon from the cache by the PokemonName. If there's not a Pokémon, then we're in trouble. Let's do something. Otherwise, we have our Pokémon and we can render it out.

[01:18] Here if I say Mew stuff is hi, and then we go here and say Mew, then we're going get stuff is hi. If we go and say Pikachu, for example, then we're going to get unknown.

[01:32] How do we call this fetchPokemon so that we can load up our cache with real actual data, rather than having to hardcode some fake data in here? What we're going to do is write in our render method.

[01:43] We'll say Const promise = fetchPokemon. We'll pass the PokemonName. Then, when that has resolved, we'll take that Pokémon that it resolves to. We'll say, Cache@PokemonName = that Pokémon that we've resolved to.

[02:03] Then here's the real trick. We're going to throw that promise. When we throw this promise, React will catch that promise and find the closest Suspense component and use its fallback to render that instead until the Pokémon has been loaded.

[02:18] When this promise resolves, then Suspense is going to re-render its children. At that point in time, because our promise set that Pokémon data in the cache by the PokemonName, the next time this renders, it's going to check the cache by that PokemonName, and it will have that Pokémon's data.

[02:36] Let's go and save this. We'll give this a try. We'll say Mew and submit that. We get all of the data. If we say Pikachu, then we'll get Pikachu's data. Now, if we say Mew again, that data is already in the cache, so it loads instantly.

[02:52] If we say Mew2, then it's going to take some time before it actually loads the data, and we'll see that loading state. This is definitely not something that you would want to actually do in production. There are a couple of problems here.

[03:03] For example, if this Pokémon info were to be re-rendered two times in a row before this fetchPokemon resolves, then we're going to fire off two promises. That's not optimal. There are a whole bunch of other concerns around cache and validation that are also suboptimal.

[03:19] In addition, this is definitely not a code that I would want to write in my function components, but hopefully this gives you a good idea of what's going on under the hood when we start using other abstractions like React Cache.

Vitaly Kondratiev
Vitaly Kondratiev
~ 5 years ago

Kent, could you please cover the negative scenario too? e.g. when promise rejects.

Kent C. Dodds
Kent C. Doddsinstructor
~ 5 years ago

That's a great suggestion. I'll make sure to cover that in the future.

For now, you just wrap it in an ErrorBoundary and that'll catch any errors the resource throws (resources should not typically attempt to handle the error gracefully)

Vitaly Kondratiev
Vitaly Kondratiev
~ 5 years ago

ErrorBoundary does help as well as wrapping a read operation in try / catch as an alternative. The problem arises if a user wants to retry fetch — that's where the issue is at the moment. Resource caches error responses and there is no way to invalidate itin the current version.

Kent C. Dodds
Kent C. Doddsinstructor
~ 5 years ago

I know that cache invalidation is something that react-cache will probably handle. We'll see what it's like when react-cache is stable :)

Halian Vilela
Halian Vilela
~ 5 years ago

So, I'm curious on what would be a production ready solution for the "double promise" issue and all the caveats you said...

Tony Brown
Tony Brown
~ 2 years ago

This isn't working. I'm getting a CORs error :(

Markdown supported.
Become a member to join the discussionEnroll Today