Transcript
00:00 we're going to make this experience way better. So let's remove this import. So we're not even getting the cities or match sorter in our main thread, that's going to be in the worker thread. We've got our worker thread here now, so we need to get search cities from that.
00:16 Then now we're going to actually initialize the initial result. So we'll just do it right here in the root of the module. So I'm going to make initial cities promise, and that will be search cities with an empty string. I'm not awaiting this, so this is a promise. It's important that we do that so that we don't like stall
00:35 showing anything to the user until that promise is resolved. But yeah, we'll just stick it right there. Now we're going to make a new app component. So right now this is our app, we're going to change this to a city chooser, and we no longer need to export it either. Now we're going to export a function called app, and this is going to return
00:55 a suspense boundary for our city chooser. Here it renders a suspense boundary with fallback of loading, and in that suspense boundary we render our city chooser component. So that way when the city chooser first tries to resolve these initial cities, it has something to show.
01:13 So with that, now we'll create a new state that'll manage our state of our promise. We're also going to bring in a use transition. So I'm going to say const is transition pending and start transition,
01:31 and that will come from use transition, and that comes from React. Awesome. I'm going to scooch this over just a bit so we can all see it so much better. So now we need to manage the promise. So as we're performing new searches, we are going to have a different promise that we're going to be tracking for resolving those cities.
01:51 So we'll have actually just a use state for our city's promise, and that initial city's promise is what we're going to initialize to. Awesome. Then we'll get rid of the use memo, and instead we're just going to use the city's promise. So this is the thing that's going to trigger
02:10 suspense boundary on our initial load, and then thereafter we're going to trigger the suspense boundary using our transition. So we won't trigger this suspense boundary, and instead we'll have the is transition pending. Okay, great. So we'll get to this extra credit here in a bit for avoiding flash of pending states.
02:29 Now, where we actually set the input value, and this is where we're going to trigger the search. We're going to go say, hey, WebWorker, here's my new search term. I want you to give me the cities, and so this is where we're going to create our promise. We want to do this inside of a transition so that we keep
02:49 the old UI up and we can show a pending state for that, and then once the promise resolves, then we show the new UI. So we're going to start a transition, and inside of this, we'll call search cities, and that's going to give us back a promise,
03:07 and this promise is what we'll set to set city's promise, with the promise we get back from search cities. Because this triggers suspense when it re-renders right there, and because it's done inside of this transition, we're going to get our transition pending.
03:27 So right down here, because we don't have spend delay here yet, I'm going to just call this as pending, and right down here, we'll add style of opacity 0.6 if it's pending, or one if it's not, and let's one-line that thing, can I?
03:47 Oh, beautiful. I know some of you look at that and you're like, I liked it better before. That's fine. You can do it that way. Okay, great. I think we're done. So let's take a look at the, or just feel it for ourselves. Salt Lake, wow, that's interesting. You might be noticing a flash of pending states. Yeah, that I hate a lot.
04:06 So we'll look at that here in a second. But let's just look at what the performance looks like. So we've got a 6x slowdown. I start my recording and I type salt and boom. Look at this. Okay, so we do still have red. We are doing a lot of work, but it is way, way faster than it was before. And again, we are in a 6x slowdown.
04:27 We're running in development mode. You could run this in production mode and see if that makes things better. It certainly would, but I am very encouraged by this improvement. And a lot of performance work is just, is it perfect? Probably not. It can always be faster, but is it better? Then yeah, let's keep going in this direction. This is a good direction.
04:46 And yeah, the code is more complex than it was before, but it's not really all that bad. It's still reasonably easy to follow. I feel really good about this. Now let's get rid of this nonsense that we've got with this flash of pending state. I hate that a lot. So we're gonna use a spin delay
05:04 so that we can improve that performance a little bit. So spin or use spin delay coming from spin delay. And we'll pass our is, well, I wanna rename this to isTransitionPending. We'll pass our isTransitionPending to that and isPending comes from there.
05:23 And so now with this in place, we don't get the flash of floating state. We will still get a loading state if it does take longer than the thresholds that spin delay has, but it's not gonna be flashing and that's a nice thing. And our performance is way better, way, way better than it was before.
05:42 Feel free to pull up the old version and the new version, but just the way that it feels, even that just feels a lot faster. So I'm really happy with this improvement. I think it was a good call. Well done on this. So what we did here, we created an initial city's promise. We wrapped our city chooser. It was called app before,
06:01 we call it city chooser now. We wrapped it in a suspense boundary so we can suspend on that and our initial load will have something to show. And then we added a transition and we added state for managing the promise for requesting the cities.
06:18 And now we suspend on that city's promise. We have our pending state with you spin delay to prevent a flash of pending state. And then when we change the value, we start the search for the cities inside of a transition and we display some pending UI so that the user knows that like,
06:36 we are processing your request. And that is way, way better. And in typical scenarios, you don't even end up showing that pending state because it is fast enough. And like really the main objective here was to make it still be responsive, even on a 6X slowdown.
06:54 Now, if we go to like a 4X slowdown, which may be a little bit better or more reliable or closer to your user's actually experience, it's gonna be even faster. And then of course, if you have no slowdown whatsoever, then it's just gonna be screaming fast. So you should feel really good about the changes that we've made,
07:14 moving things over to a WebWorker, handling the asynchrony with the awesome APIs that React gives you for doing that. And now our experience is way, way faster, not just re-computing existing values and avoiding things with useMemo, avoiding unnecessary computation with useMemo, but even just the process
07:33 of performing those actual computations. And then of course, you could add your own caching on top of all of this, if that was even still more important to you, have fun with that. But I think that the experience that we've got here now is quite good and I'm happy with it.