Use React Suspense to Simplify Your Async UI with Kent C. Dodds
Instructor: [0:00] Hello, everyone. This is exciting. Welcome to our workshop today, it's going to be great. We're going to talk about concurrent React. Let me get my screen shared before I forget because I do very often forget to share my screen.
[0:15] If you're ever not looking at my screen, it's a mistake because I'm always assuming that my screen is being shared. If you ever don't see my screen, then let me know because I do make that mistake sometimes.
[0:28] We're going to talk about concurrent React, suspense. It's going to be awesome. Just a couple of housekeeping things. They are boring right here at the beginning. Hopefully, you got the whole thing all setup.
[0:41] I was making last minute changes last night, so if you got it setup yesterday during the day, just do a git pull and you shouldn't have to rerun the setup. I didn't change any dependencies or anything. If you just pulled away these changes, that would be great.
[1:00] These workshop is a bunch of exercises. The idea is I give you the exercise that gets you working, gets your brain in the right frame of mind, so that we can do it together. Then, you have all the questions that you need to ask.
[1:15] It's not about getting through all of the exercise necessarily or getting through the extra credit that we have. It's mostly about getting into the right mindset so that you can do learning that you need to do.
[1:31] Sorry. I'm just fixing my camera. Now you know that this is fake. That's a green screen. I'm not really out in the woods. Yeah.
[1:43] That's the idea behind the workshop. It's not a solo venture either. I'm going to split you up into groups. Some people find that really helpful and others don't. I find that the people who find it to be helpful are the ones who are actually talking and working together through some things.
[2:03] Or at least to have their video turned on and bring up observations that they have. I do encourage you to interact with each other, even though we're not in person.
[2:15] I'm not going to be lecturing a ton. I want there to be a lot of interaction here and it's not just one day. I recommend that after we finish our workshop here, next week, you come back and go through all the material again. That will help solidify the things that you've learned here. Yes, strong recommendation for that.
[2:37] Here's what our schedule is going to be. Right now, we're starting with the boring logistics. Then we're going to get into simple data fetching with suspense and concurrent mode. That will be low level stuff.
[2:50] There's actually one particular thing in there that's an API that may change. Just a reminder, this is all experimental, unreleased stuff that we're learning today. Things can change before it's really stable. Actually, there are bugs that I will mention throughout the workshop as well that you have to work around. Not a lot, but a couple.
[3:14] Then we'll have a break. We'll talk about render as you fetch this new paradigm or approach that Suspense makes reasonably possible. It's really cool. Use transition is a new hook. We're going to talk about and have an exercise for using that feature. Then we're going to take a longer break.
[3:33] Normally, my workshops are like, started like two hours ago or an hour and a half ago. Putting the 30-minute break right here made sense. Especially those of you in the East Coast, you've actually probably already had lunch. It's 11 o'clock, my time. For those of you on the West Coast, probably it's 10 o'clock.
[3:56] All I'm saying is, we might move this 30-minute break earlier. Just depends. I'll put it up to a vote when the time comes. In any case, we are going to have a 30-minute break that is intended for you to step away from the computer and talk or get some food or whatever.
[4:16] Yes, if you happen to be in Australia, like Luke is, then it is...I don't even know what to do for you. [laughs] Thanks for waking up. Holy mackerel, that's, wow, dedication. You got to learn this stuff.
[4:31] Wow, we got Croatia, 7:00 PM. Yeah, you'll probably be all right. It'll be a little bit late when we're done, but [laughs] yeah, cool. That 30-minute break, it'll happen eventually, at some point.
[4:46] Then we're going to talk about using images for suspense. They're a unique thing. The React team is actually working on some specific stuff for images. We may have some extra primitives to make this a little easier. There's definitely a way to make images work really nicely with Suspense. We're going to talk about that.
[5:14] Then we'll have another break and then our cash, the resources. We can even stick it in into context we got extra credit for that. Then coordinating suspended components with suspense list. It's going to be great. That's all the subjects we're going to be covering today.
[5:30] One thing, normally my workshops have tests written, but I didn't get to the test. You're not going to have the tests to run all inside to make sure that you're getting things done correctly. Most of the things are hard to test for anyway.
[5:47] There are lots of different ways that you can implement this stuff so I'm not sure how useful the test will be anyway. You'll just be running start. Go ahead and pull up your [inaudible] right now and run npm runner. Start to get your dev server started. This will pop open your browser and this is the app. Let me just take you on a quick tour of the app.
[6:12] If you click on the title of one of our exercises, that will take you to the exercises page, if there's not an error on it which of course there is isn't error on this page. That's because the exercise is busted, but I thought I had an error boundary. No, I removed the error boundary because you're supposed to add that for the exercise. My bad.
[6:32] Going back to the home page then, it goes straight to the exercise or the final version. If we look at the final, this is what you're going to be building so you can compare that. You can pull this up in two different tabs to see the before and after.
[6:51] Your job is just to make the exercise version look exactly like the final. Look and function exactly the same way. We've got that for all of these. In addition, if you don't want to navigate in this way, on at the top of every one of our files so if you go to source exercises, this is where all of our exercises are going to be.
[7:13] At the very top is a link to the isolated page for that exercise. You can just copy that, go directly to that exercise page. Your component is the third component rendered on this page so you can use your dev tools. It should be pretty easy and straightforward there.
[7:34] A couple ways to navigate around with the app there. As far as asking questions is concerned, I do strongly encourage that you take the opportunity to ask. This is one of the benefits to doing a workshop rather than just watching a recording is you get to ask me questions. Please do interrupt me.
[7:58] You can write comments in the chat but I often don't see them. In fact, I just noticed a comment that I missed earlier. Somebody was asking about where they can get the material. It was emailed to you so hopefully you get.
[8:18] I often don't see the channel. I try to keep it open and look at it, but I often don't see it. Please do interrupt me as I'm speaking. If you have unrelated questions that you don't want to like the delay or things then you can feel free to ask those things on my AMA.
[8:35] Then I'll presume you're all doing a great job everybody that I can see anyway keeping your video on if possible, keep your mic muted unless you're speaking and then we'll do you breakout rooms which hopefully you watched the video about that earlier.
[8:50] The exercises. There are couple files relevant for each exercise. For these tests, those tests aren't working right now. We have marked down file that's where we get started and that will give us background exercise instructions and extra credit information. Then the actual exercise is right next to it the same number.
[9:12] The exercise has emoji throughout explaining different things that you need to do for the exercise. For this one, this particular workshop we have a hacked version of window.fetch so that when you make fetch requested to the Pokémon that we're going to be interacting with, we can have better timing on that.
[9:35] Everything actually will work locally even without a network connection. That's what all these instructions are and those instructions are in every single one of the exercises. The reason we do this is so you can more finely tune how long it takes to resolve requests and stuff so you can play around with that.
[9:53] I definitely recommend playing around with this fetch time variable here. If you want to make an actual request to a real server, a real backend, then you can call this "restore original fetch." That will make an actual request to an actual back end, which will be useful sometimes when you want to see an actual network activity going on. Useful stuff there.
[10:18] In each exercise, you've got this emoji telling you where to do stuff and what to do there. The idea is that I make it as easy and hand hold-y as possible because I don't actually teach you stuff until after you work through it.
[10:36] This is very intentional, it is pretty uncomfortable for people sometimes. The science seems to suggest that the more that you struggle with the concepts, the better you'll remember it. I'm interested in not only just regurgitating a bunch of information at you, but I'm actually interested in that sticking for you.
[10:58] That's why you're going to be jumping into stuff that you're not really familiar with that might be a little challenging, but that's the point. I do have the emoji there to help guide you along the way. If it feels hand hold-y, it kind of is.
[11:15] There is of course a final version, if you look at the exercise's final directory, you can see that final version. There's also extra credit, the final version of the extra credit in there as well. You can compare if you want to jump into that. I'm going to work through everything that we have time for anyway, so this should be totally fine.
[11:36] That's pretty much it for all the logistics around Jest general workshop stuff. First, before we get into the actual exercise, I want to give a couple disclaimers that you should know.
[11:50] First off, React concurrent mode is experimental. We have installed in this project if you look in the package JSON, we're using an experimental version of React. They just barely created this experimental release channel for us to play with this stuff.
[12:08] That means a couple of things. First it means that things that you learn today could actually change. There were some things that probably will change. Just keep that in mind. Another thing is I should also mention that the concepts that you'll be learning today, conceptually, those will not change, but some specifics of APIs might change. Keep that in mind.
[12:38] Another thing that experimental React concurrent mode means is that I have not shipped any of this stuff to production before. Neither is anybody else, except for Facebook. They're experimenting it with it themselves. Keep that in mind as well.
[12:56] It's not running in production for anybody. Even for Facebook, they're only using this stuff on the new facebook.com, which a very small percentage of their users are using right now. It's a really experimental technology. It's based on years of research that they've been doing and working on for a long time and it's awesome.
[13:18] Also, there are no really awesome abstractions and React suspense is a particularly primitive API. As amazing as it is, it can be a little bit tricky. Abstractions will be built on top of this and already are actually, there are at least three, four that I can think of right off the top of my head. Abstractions that build on top of React suspense in concurrent mode for data fetching.
[13:51] They're really awesome, but again, it's all experimental. The primitive APIs are a little but on the awkward side, but once you have those abstractions...What's so cool about suspense is it is so primitive that building abstractions on top of it allows you to make really powerful abstractions that are easy to use.
[14:15] We're mostly going to be focusing on the primitive API and then you can go off and build your own abstractions. That's the idea. Yeah. If you ask me questions and I say, "I don't know," I told you so. There are lots of unknowns around this stuff. Just prepare yourself for that.
[14:34] At the very end of the workshop, we'll come back to this file. This is the outline MD files and you can give me this feedback, except that URL's wrong. I'll give you a new URL, just in the chat for you to give me some feedback. I'd really appreciate that. Cool.
[14:52] I'm going to open up the exercise Markdown file here and invite you all to do the same. While we're waiting for everybody to get on the same page, does anyone have questions about anything? All right, sweet.
[15:11] I'm going to open up the final version first, this first exercise. We've got a little Pikachu that loads up when we get started. You'll notice, there are actually three states. When I hit refresh, we get this loading in the middle. That's actually the workshop app that shows that. The workshop app uses suspense to load the code.
[15:35] This code runs in total isolation from the rest of the code in the project. That's what we're doing. First is loading this code. Then once that loads, then the component itself that you're going to be writing is going to suspend to go get the information for Pikachu. That's what the exercise is all about.
[15:55] A little bit of background. We're going to be as totally simple as possible. This is so that you can learn the basic API for making a component suspend or stop rendering. This is the basic API. If you have some data, then you render the data. If there was an error getting the data, then you throw that error and let an air boundary higher up in the tree catch that.
[16:24] If there no data and no error, then you throw the promise that is making the request to get the data. React will catch that promise, find the closes suspense component and suspend everything from that component on down.
[16:43] Suspending basically means it doesn't matter what state changes happen between now and when that promise resolves. It will just stay there until that promise resolves. There's also some logic around showing a fallback after a certain amount of time that React manages on its own there too. That's the basic idea.
[17:07] I was talking with Dan Abramov and he said that this API is likely to change because it's easy to get things wrong here. They're going to try and do something, but I'm pretty confident in telling you that you will be throwing something, whether it's a promise or something else.
[17:25] I can't really be sure, but you will have to throw something because you need to stop the rendering from happening. That's the basic idea of how you make a component suspend.
[17:40] I totally forgot and I even missed this, right here. Nobody caught me on this, but I forgot to show you how to enable concurrent mode because that's important. You can't really use suspense in this way without enabling concurrent mode.
[17:55] Let me show you that really quick. It is really quick. In fact, I'll show you it on the blog first. I wrote this blog post just this week. You can go read it. It's interesting. The key thing here is this is all that you change in your app.
[18:10] Normally, you're going to ReactDOM.render your UI to an element. Now, to enable concurrent mode, you're going to say ReactDOM.createRoot. You pass it an element. That root can render the UI. That's all that we're doing to enable Suspense. For data fetching, right here, it's just right here. Ta-da.
[18:30] Did you know that you can give element IDs emoji. Element IDs can be emoji. That's fun. If you look at the index.html, it's right here. Ta-da. That's our app. React. Kind of fun.
[18:47] Once you have that enabled, then you can start throwing promises around. Then your React Suspense boundaries, they don't really catch the promise. React is the thing that catches the promise.
[18:58] Then it finds the Suspense boundary closest in the tree to that component and then applies the fallback after a given amount of time. Everything that's inside of here does not re-render until that suspending component is able to render again when that promise resolves.
[19:21] That's the basic idea. The key is where that data error and promise values are coming from. That's what you're going to implement in the exercise. To think about this conceptually, what we're going to be doing...Consider a scenario where you need to have some data load before your app is at all useful. This is like your bootstrap data for the user's information or something like that.
[19:52] Typically, what we're going to do is we'll put it inside of a React useEffect, something that looks like this, to go request the data. Then we render stuff based on that data or based on that state. If we're in a pending state, then we're going to render a loading spinner or something like that.
[20:07] Typically, bootstrap data doesn't need React to mount your component before you can actually start the request. This is starting to get into the render-as-you-fetch approach that we'll be talking about a little bit. That's one of the key things for what we're going to be doing in this exercise. We're actually going to start the request before React even renders anything.
[20:32] This is a quick review of promises, if that's new to you. For our exercise, we've got that Pokémon page for the Pokémon named Pikachu. We're going to load Pikachu's data as soon as the app gets going. You need to have an error boundary.
[20:51] I built one for you. It's not really as relevant as error boundaries are for Suspense. They long predate Suspense. We're not going to get into it in the workshop. You can look at the error boundary if you want, but you just import it.
[21:06] You'll also be using the React Suspense API. There are links to docs for some of this stuff if you want to dive into that, but the emoji should help you know what you need to do for some of this stuff.
[21:19] Before I set you free on the exercise and getting through some of the extra credit and stuff, somebody asked in the chat here if I wanted to explain the why behind Suspense and everything. I am really sorry. We don't have a lot of time to get into it.
[21:36] This is why I had all of the prerequisites, things that I wanted you to do, because this talk right here will give you the why. I'm not going to really talk about the why. That talk, that was what that was for. This is the how.
[21:53] Sorry about that. If you do have questions after we work through this exercise and talk a little bit about it, then we can. We've got lots to do. I'm not going to dive too much into that.
[22:07] Any questions before I set you off on this first exercise? OK, cool. Let me just split you up into a couple rooms here. I'll jump around each one of the rooms to make sure everybody is good. I'll leave you alone so you can work on the exercise. Have fun.
[22:40] All right, folks. We're coming back. Everybody has 60 seconds to get back. [hums]
Audience Member: [22:54] I guess it's the time for that joke.
Instructor: [22:56] Oh yeah.
Audience Member: [22:57] I want the video.
[22:58] [laughter]
Instructor: [22:59] That's right.
Audience Member: [23:00] Impressions.
Instructor: [23:00] [laughs] Why did the chicken cross the road? To get to the other side. Why else would a chicken cross a road? I don't know. My daughter, when she was two years old, she thought that was hilarious. May have had something to do with the fact that we were laughing. Little kids just laugh because their parents are laughing. It's so cute.
[23:25] Maybe next time I'll look up jokes. Last week, I did a Halloween-themed workshop because it was on Halloween. I dressed up as Harry Potter. I would do spells while I waited for everybody to get back. That was fun.
[23:39] All right, cool. Let's run through this data fetching example. The exercise version of this totally busted because we don't have the Pokémon data. Pokémon is not defined. Let's go ahead and make that not busted.
[23:56] The first thing I'm actually going to do is I'm going to put that in an error boundary because the error boundary is going to take care of our Suspense errors in addition to our regular errors that are just thrown during render or life cycles or any of that.
[24:10] First thing I'm going to do is some up here and nap this error boundary. Thank you, Marty the Money Bag. I'll grab the error boundary, bring it down here, and put the Pokémon info in there. With that now, we at least get this thing displayed. Now we see the error right there. If you hit "Try again," all that does is it triggers a rerender.
[24:37] Maybe, magically, it will work this time. Of course, it doesn't because Pokémon is still not defined. Let's go ahead and fix that. One thing that we could do is just say "Pokémon" and define it, but that's not going to do it. We need to get the Pokémon from a server.
[24:54] We do have a request that we can make from this fetchPokémon function. Actually, I'm already importing util. I'm going to just move that right there. There we go. We got our fetchPokémon. We don't need to have our component render before we know which Pokémon. We want to get Pikachu. We can fire that off as soon as this module is loaded.
[25:19] That's not typically something that I encourage people to do actually. I even have a blog post about how it's important to keep your modules pure, meaning there are no side effects just by loading your module. It makes it pretty hard to test if you do that.
[25:36] In a real app, if I were to have something like this, I'd probably put this, put the code for the component, in a separate module from the code that fires off the request, but we're just going to do this right here to make things easier for us.
[25:52] The first thing I'm going to do is we're going to let all of these things. I don't like combining declarations. I'm going to put them all right here. The Pokémon promise, actually we're going to assign that to a call to fetchPokémon for Pikachu. When that is finished, then we're going to get our result. Then we'll say our Pokémon equals the result.
[26:19] If there's an error, then we'll say the Pokémon error equals that error, so whatever the error was. Actually, let's just go ahead and we'll console.log result here. If we refresh, it's somewhere around here. There it is. Ta-da. Result, except I put it in a string. [laughs]
[26:44] Let's try that again. Boom, there it is. Whoops. Right here. Nice. All of the awesome stuff we want. We want to be able to use that Pokémon when we render. We are. We're using the variable that we defined up here.
[27:08] Once this gets set, there's nothing telling React, "OK, this thing's ready to render again." In fact, there's nothing telling React, saying, "Hey, this component isn't ready to render. It doesn't have all the data that it needs."
[27:23] What we're going to do is right here we're going to say, "If there is a Pokémon error," so if it has loaded and the error has been set, then let's just throw that. Then our error boundary can catch that.
[27:41] We can actually experiment with this really quick by saying, "Pokémon error equals a new error. Hello world." We'll save that. Then we'll get that caught by our error boundary. That's cool. We've got that all squared away. If there's an error, then we're going to get our error boundary catching that.
[28:00] Then, if there's not a Pokémon yet -- we'll say, "If not Pokémon" -- then we're going to throw the Pokémon promise. I know it's weird. I promise you that's the API for it. Again, you will not be doing this in your normal day-to-day React components. Absolutely not. This is low-level API.
[28:20] We're going to throw that promise. That is how we say, "Hey, React. This component's not ready. There's some asynchronous thing going on. We're making a request. We're looking for the user's geolocation there, like various things that we could be doing. There's an asynchronous thing going on. I'm going to give that to you. I'm throwing it."
[28:36] The reason that we're throwing it is so that the rest of this code doesn't run. The rest of the code isn't ready because the Pokémon hasn't been loaded yet. The throw will actually stop execution in JavaScript.
[28:50] React catches it because React is the one calling our function in the first place. It will take that promise and add another then onto the promise that says, "When this resolves, then we're going to trigger a re-render of this component." If it rejects, then we'll find the error boundary and throw that up to the error boundary.
[29:09] Let's save that and boom. We're totally done. That was all that we needed for this exercise. Hold it. No, that's not all. Sorry. You probably were like, "No, no, no. We're not done." No, we're not done. Sorry.
[29:25] The reason that it works is actually because we have a Suspense boundary already in the app. If we look at our components here -- let me bump that up a little bit -- we have the main app. Then we have our Suspense. Then we have our app. That's the component that we're dealing with in our file.
[29:44] Because we have this Suspense boundary right here, that's why things are working, because there's that Suspense boundary. This Suspense boundary is responsible for loading the code that you're writing.
[29:55] In a normal app, if you just didn't have a Suspense component, you're going to get an error that says, "Hey, you need Suspense if you want to throw promises around." Let's go ahead and add that really quick.
[30:07] Right down here, I'm going to add a React.Suspense. Then we'll add a fallback that just says, "Loading Pokémon." There we go. Loading Pokémon. Ta-da. Now we're done. We've got some refactorings for the extra credit and stuff, but this is fundamentally how you use Suspense for data fetching. What questions do you have about this?
Audience Member: [30:40] Is there a reason for calling the fetchPokémon outside of the component instead of in a useEffect hook?
Instructor: [30:50] Yeah. Actually, we've got an example for our next exercise. If we were to call this inside of a useEffect hook, then by the time we get right here we are already rendering the component. The promise that is created happens inside the useEffect.
[31:07] Right here, we have to actually handle all of the loading and error states ourselves because we don't have access to the promise to even throw it. That's why it's happening outside of the component. In a realistic scenario and our exercises that we'll be doing here soon, we won't be doing things outside a component on the module level.
[31:32] We will be doing things inside of event handlers to kick off promises or kick off requests and then creating these resources that we can use to do basically the same kind of thing, except it's not littering our components with throws and stuff like that. It'll look really great.
[31:50] That's why. It's because if you use a useEffect, then there's no way to stop rendering because it already has rendered. Great question. Any other questions?
[32:06] All right, sweet. Let's refactor this a little bit. Just make sure we're going in the right order here. The first extra credit is to extract the complicated bit to getPokémon. This is the complicated bit. Maybe that wasn't as clear as it should have been. That's the part that I wanted to extract to a getPokémon function.
[32:24] We're going to have getPokémon. Let's do this. We'll return the Pokémon. Now we say that. We're just going to move that out of our component. No longer does our component have any promise-throwing or anything like that. It's all been extracted. That was the first refactor we wanted to do here.
[32:52] The next thing, to take it a step further, is to make a more generic createResource function so that we can have an API that looks like this. createResource accepts a function that returns a promise. The resource that it returns has a read method on it that will give us back the data. That's the API we want to create.
[33:17] I'm going to just come right here. We'll have a function called createResource. That's going to have our async function. Let's just load all this stuff right up in here. Do that. How do we make this? We know that it needs to return an object that has a read method on it. That's actually what this getPokémon thing is doing.
[33:42] Let's move all this stuff right here. If there's an error, then we'll throw the error. If there's no Pokémon then we'll throw that. Otherwise, we'll return that. Instead of fetchPokémon, I'm just going to cut that out. We'll say Async function.
[34:05] Now if I do PokémonResource = createResource and then call fetchPokémon, then I can come down here and say PokémonResource.read to get this stuff. Let's make this further even more generic by renaming some variables. Let's call this result. We'll call this simply error. Call this one promise or suspender.
[34:37] Then we've got some variable shadowing going on here. I'm going to call this...Shoot. I don't remember what I named some of these variables. Hold on. Don't look. No peeking. Oh yeah. Just one-letter variables because that's the kind of person I am, a monster.
[34:56] Now we have our result. We have our error. We have our suspender. This looks pretty generic, right? We could use this for any asynchronous thing we want to have happen. We just pass a function, our asynchronous thing, to this createResource function. We're going to get back a thing that we can call .read on it in any of our components.
[35:21] Those will now be suspending components. All you have to do is make sure that where you're doing this, you are also rendering a Suspense with the fallback above it and an error boundary somewhere reasonable.
[35:34] Let's see. There are a couple other refactorings I want to make to this just a little bit. Any questions about what we have so far?
Audience Member: [35:44] Can you pass params to your createResource function?
Instructor: [35:49] Yeah.
Audience Member: [35:49] The factory, the generated function that you created from that.
Instructor: [35:53] We could do like an args thing and forward on the args. Then we could say fetchPokémon. We'll take all the rest of those as our args. We could do something like that.
Audience Member: [36:13] Ah, OK.
Audience Member: [36:15] What if we wanted the args to come from the props?
Instructor: [36:19] Good question. What if I want to say Read Pikachu? Am I spelling that right? Pikachu. There we go. That would totally be a possible thing that we could do, but you have to remember that one of the really crucial points here...Well, let's make this work. This could work.
[36:42] Instead of actually firing off the async function when we create the resource from the get-go, we just have a...Let's see. We'll just do this. We'll say Let suspender be nothing.
[37:05] I'm thinking of like six different things. Let me back up a second. I'll do a refactor. Then we'll support this. I'll explain why this is not what we want to do. It will help with things. One other refactor I wanted to make to this...To avoid states that are invalid, one thing that I want to have here is a status.
[37:33] The status is going to initialize as loading or -- let's see -- pending. We'll have a status of success in the success case and a status of error for the error case. Instead of keying off of whether an error exists or whether there is a result or anything like that, we're going to be a little bit more explicit.
[37:54] We'll say if status is an error or if the status is pending or if the status is success, then we'll do this. If we get past this part, then we'll throw, "Impossible." Who knows? That shouldn't be able to happen, but if it does then we'll get an error that is not a very helpful error message. In a real world thing, please write a better error message there.
[38:29] We can make things even weirder, if you want to. This is what I do in my final solution to this. Because why not? I don't know. I think it looks OK. That should work. Our Pokémon thing should still load just as easily.
[38:49] To answer the question, what if we wanted Pikachu to come from here? That's a great question. In our read function, we're going to take the Pokémon name. Instead of doing this eager call to our async function, I'm going to come down here.
[39:11] Let's see. If it's success...Actually, it's neither error nor pending nor success, so we won't initialize it. If none of those things happen, then we'll set our suspender to the async function, and we'll set our status to pending. We'll need to declare that suspender.
[39:36] That should work. If we go over here...Kaplowee. This is cool, but here's why it's probably not what you want. Actually, this works fine. The reason that I wouldn't recommend this is because the way that this is built is the Pokémon info has to render before the request is made.
[40:02] That means that not only this file needs to load but all the other files in your project need to load. Then React needs to render all those things before it actually kicks off the request for this resource. There's no reason to delay because we know what this value is.
[40:24] Now, if it's coming from props, then that's slightly different. There's a different approach to accomplish firing off the request as soon as you have the data. We're going to talk about that in our next exercise. That's why we're not going to have this API in the long term. It's a great question. Thanks for asking. Any other questions?
Audience Member: [40:51] More of a comment. You could probably wrap Pokémon resource with another function that takes your params and then returns that. You know what I mean?
Instructor: [41:04] Yeah. You can have a...Actually, we do this later, create Pokémon resource that takes a name and then returns this.
Audience Member: [41:18] Yup.
Instructor: [41:18] Then we could just say create Pokémon resource Pikachu. We actually do this later.
Audience Member: [41:26] I was thinking of more like putting that create Pokémon resource inside the component. That way, you can do the prop.
Instructor: [41:37] I'm glad that you mentioned this because this is an important thing to recognize. A key thing to remember with concurrent mode is that you have no guarantee on how many times your component is going to render, how many times a render call is going to be made.
[41:59] When I was preparing this workshop, I have this crazy stuff going on in this app file. You can go explore it later. We have this use isolated component thing that allows me to dynamically determine what exercises there are and then create special pages just for those using React.lazy.
[42:23] I was creating React.lazy even in a useMemo inside of another component. That's what I was doing before. I changed it to this approach so that I could avoid the problem that I ran into. What happened was I was creating a new promise on every single time the render happened. I would throw that promise and then react to it, attach it then onto that promise that I threw.
[42:49] I'm not exactly sure why, but it would trigger another render. I've created a new promise, throw that, react then on that and just kept going for, like, infinity. It was crazy. One of the important things to remember is that if you create a promise during render, you need to make sure that you're caching your promises, and you always return the same promise that you did the last time.
[43:14] That's the thing that's easy to get wrong, which is one of the reasons why the React team is planning on making a change to the API in some way to make it so that doesn't happen as easily. This will probably look the same.
[43:29] My guess is they're going to have some sort of special create suspender API or something like that to reduce the likelihood of this happening. I'm not sure how that'll work. I'm not on the React team for a good reason. [laughs]
[43:44] That's why we're not going to do it that way. We're going to keep it out of our render phase. We don't want to create a promise during a render. Now, one of our exercises -- I think exercise four -- will get into creating a promise during render, but it'll set up some caching to avoid the problem.
[44:04] Then you have an extra credit to make that situation even better too. Lots of exciting and new mind-bending things here.
[44:13] We're out of time for questions. If you do have further questions, feel free to ping me elsewhere, but we needed to get moving. Before we do, scroll down here in exercise. There should be a link to write down the things that you learn, really important that you do that.
[44:30] Go ahead and take a minute or so. Write down the things that you learned. It also asks for some feedback, which I appreciate. Writing the things that you learn will help you retain the things that you learn. This is scientifically proven. That's why I make you do this. Render that, and then we'll start with exercise two.