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
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.
Kent, could you please cover the negative scenario too? e.g. when promise rejects.
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)
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.
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 :)
So, I'm curious on what would be a production ready solution for the "double promise" issue and all the caveats you said...
This isn't working. I'm getting a CORs error :(