Lesson Description
The "Optimistic UI Updates" Lesson is part of the full, React Performance, v2 course featured in this preview video. Here's what you'd learn in this lesson:
Steve discusses optimistic updates, showing how to give users immediate feedback while waiting for server responses. He also demonstrates implementing them in React and explains how they improve user experience and app performance.
Transcript from the "Optimistic UI Updates" Lesson
[00:00:00]
>> Steve Kinney: So we're going to check out another example in our tour of examples. This one is called Antisocial Network. It is a blogging platform where you can only talk to yourself. I don't know, you know, sometimes coming up with ideas for apps isn't easy. Leave me alone. So we'll go ahead and we'll cancel this one, npm run examples, Antisocial Network, npm r dev.
[00:00:42]
And it's almost like I built a component library, use the same components for every single app that we built today, because I did. Okay, so this is currently not optimized. If this is using a real API, so if you're not on the internet right now, which I've got some questions on how you're watching this, but you know, it is using the JSON placeholder API, so you do need to be on the internet.
[00:01:14]
And right now it is not hooked up to anything. And we will fix that momentarily, but let's see it first. So I'll say like, hello world. I am optimistic. I'm not actually, because we haven't implemented that. That was a little fast. Let's slow down the network. Let's be dramatic. I'm going to put it on 3G, which like one room in my house, I don't know.
[00:01:50]
I need to keep the developer tools open, otherwise it does not throttle my network otherwise, but we'll put them on the side there. And we'll say there looks like there's also some, a little bit of memoization if somebody wants to go on a bonus lap at some point. Okay, so I hit post. And then I waited and that felt gross. I'll do it one more time for dramatic effect.
[00:02:36]
I'm going to click it and I'm going to move my hands up. Too long, too long. What was happening? If I didn't see, if I didn't have the render element thing going on, I would be like, the internet's broken, this entire app's not working, and submitted my thing like 1000 times. There was one user monitoring tool that I use, or I didn't use, the designers used like seven jobs ago, and one of the metrics was rage clicking.
[00:03:07]
Right, it would send an alert if somebody was slamming on the key, right? And that is a situation, both the search that you saw earlier, and also where there's just no feedback whatsoever, right, is where rage clicking comes from. That's a UX problem, because we're not going to solve the fact that the internet is slow, we're simply going to lie to the user.
[00:03:29]
And sometimes you just want to be lied to. We're going to go ahead and again, the really great part about all of this is because a lot of the hard and sophisticated work is done in fiber. Like the easy stuff like pushing your state down was a lot more tedious and took a lot more work than some of the sophisticated stuff, which is again, going to be like 34 lines of code grand total, right?
[00:03:55]
That's not to say that there's not a lot of sophistication behind it, but it's going to be one of those things where you're like, yo, I could use that on a feature tomorrow. And then I've done my job. Cool, cool, cool. So let's go ahead and try it out. Let's hop into the code at this point, Antisocial Network. Okay, so I've got, you know, a current user ID that's fixed because it's a fake demo.
[00:04:24]
We've got all of this in place. And so what we are going to do is we've got this set of posts and what we want to do is also have an optimistic one as well, right? So what we're going to do is, let's, how do I want to go about this? I'm going to go, I think I'm going to put it right here, and what we're going to do is we'll have, let me kind of write this a little bit backwards.
[00:05:04]
We're going to do useOptimistic, right? I'm going to assign that to a value in a second, so no one let me forget to do that. And this is going to be with our posts. And we're going to say that if you've ever used a reducer, this is not unlike that, we need a function. So we're going to have the initial state, which is our list of posts. Right, because ideally when you submit a post, it's going to eventually when that promise resolved, if we look at it in this useEffect, that's fetch posts and handle create post, we create a new post, we pop it onto the array, right?
[00:05:47]
And so this is kind of like if you've used useReducer before, not unlike that where the two values that go in are going to be, you know, all the posts which I have, we'll do the, I'll call it state, or current state or something like that. I'm just naming it something different than post so I don't go insane while I'm live coding and talking at the same time, and we'll say new post.
[00:06:12]
Do I want to call it current plus? I'm not changing the name of it. So yeah, you have the kind of current state of the world, that new thing that is going to happen, we hope optimistically, right? And so we can go ahead and we take that and we can basically say, and new post has some fun little things going on, which it's got an is, like there's an optimistic version of it as well, which we can kind of see in the types, which if you look at it, it simply has is pending on it as well.
[00:07:05]
So we'll actually put that in there. And we'll say if new post is pending, well then we'll return effectively what would have happened, we'll put at the top in the rest of the state. Now, what's cool about this is it's not actually modifying the post state. Right, it is simply saying like, in this temporary world, take the current state with this optimistic modification of what was going to happen.
[00:07:40]
What this allows React to do is if this goes poorly, it kept a copy of the real state. Right. And is able to like, all right, never mind, never mind, bad news, not happening, takes you back seat, right? Otherwise, filter it out of the array. So we kind of have the, hey, is it pending? Otherwise, we don't want it in there, because even if it succeeds, it's still going on to the array.
[00:08:21]
Right? So this is only our fictitious optimistic one that we're going to put it on there. When this resolves in any way, shape or form, we want to swap it out with the real one. Cool, and so do in this case as well. And so here we'll do optimistic post at optim, I could have found it easier to spell word for that while typing and talking, but here we are.
[00:09:17]
Cool. So we've got that in place, and so, cool, cool, cool. I think we've got most of what we want there. Boo, and then we'll have this useEffect. So this comes in, we set loading is true, that's all good, we list the posts, that's all cool, we set the post, that's great. The error is normal, awesome. And then on the handle create post, we've got the errors and all, and so what we're going to do is we're going to create a temporary post.
[00:09:55]
So when they go to create one, we'll go ahead and we'll make that. So now, before, if you look before, we are literally immediately calling the fetch request. At this point, we're going to say like, we assume things are going to go well. So we're going to optimistically render it. If this promise rejects for some reason, like, the error will bubble up to an error boundary or whatever, but we will remove our optimism, right?
[00:10:30]
So the user will see it immediately appear. If the promise is successful, well, we swap it out with the real one and life goes on. If it's a failure, it, you know, either blows up to an error boundary or something, and we also remove it. So we kind of just put a temporary placeholder so they get immediate dopamine hit. We do all the long stuff, and then we reconcile the actual reality at the same timeline that it was already going to happen.
[00:11:03]
So optimistic post in this case, again, we do need to define what it is, right? So we'll say, and because if you notice the way we're getting rid of it in this case is I'm just filtering out its ID. Right, and so it doesn't necessarily have to have the same ID as the one the server is going to give it. There's a bunch of ways you can do this.
[00:11:28]
You can use, I don't know, a counter, anything you want. My pattern and you don't have to do this, let me actually just see. Optimistic post extends API post. I gotta check with my own. Previous me decided to do for what is a post. User ID is an ID. I hate past me sometimes. We're just going to give it, we're going to cheat a little bit here.
[00:11:59]
On a TypeScript level, not on a, what I normally do is this only works if you are targeting fairly modern browsers. You can do a Date.now. You can do any kind of, you can even pull in like a UUID library. If you are using a modern-ish browser, there is crypto.randomUUID which does what it says on the tin. It will make a random user ID, because I got fancy because I was also using this for the TypeScript stuff that I'm doing in the future.
[00:12:53]
Shush, we'll ignore the TypeScript errors, so right now, title is form data. I get title. That's form data title. And, you know, all the stuff that would, I really could just copy and paste them down here. Form the body. Anything else that we need in this one, user ID is current user ID I said earlier. And is pending, it's going to be true.
[00:13:47]
Put those in there for there for now, so temporary, we've got that, we're rocking and rolling. So now we have our fake one that we can use while we wait for the real one to come in. So what we do in this case is we have, so let's just talk through this. Okay, user hits create post. We go in, we make a post that is basically what we'd expect to get back from the server.
[00:14:08]
We can show that in the UI immediately, they get the feedback. Because again, if you have a flaky UI that could fail all the time, are you doing anyone any favors by doing this? No. But for things that we generally speaking, work most of the time, we can get the immediate feedback while we wait for the server to come back, right? So we add that in there, and then we go ahead and we wait for the real thing to happen.
[00:15:17]
I think that should do most of it for us, unless I am forgetting something, but I think that will do it. We'll find out together. Did I save my file? I did. This is how people used to live. Me on my iPhone. 2G originally. There were no apps at the time. All right, we're, yeah, great. I remember rehearsing the other day, and be like, I can't live like this and turning it off.
[00:16:43]
That's good. Anyway, we can see how painful it is, that at least shows us that. Let me take a look. Did I, all right, so I did. I did add it on there. Right? I've got, I'm using everything. I'm using my optimistic post, I use my optimistic post, that's cool, great, great, great. Putting at the top of the array. Let's just do a quick console log.
[00:17:13]
Make sure I got all that right. So some quick tasty notes while I'm here though. You can kind of see that it does remind us a little bit of like useEffect. Right, you have what should happen and then it kind of returns what it should do when all that finishes. Okay, just fixing those little TypeScript issues. Okay, so the only thing that I missed because while I was talking was, you know, this is, you know, insofar that we have those kind of changes is that this too happens inside of a transition, right?
[00:18:03]
And, you know, if I remove this, let's see, let's just take out the beginning and the end there. All right, yeah, that's all still valid, great. You will see, and I'm going to turn off the throttle lane for a second. You can see that we do get an error to the console, which is an optimistic update occurred outside of transition or action.
[00:18:26]
To fix, move the action or move, move the update to an action or for our cases right now, wrap it in a transition itself, right? So the idea of like, this is a, hey, show the optimistic update, which is not a high priority thing, right, versus other renders, and then it will resolve all of that. So really all I had to do beyond what I had earlier was wrap this function in the idea that it is kind of this, that way you do get the pending state and everything else like that too, which is kind of cool.
[00:19:01]
So if you've got this is pending, we could do something very similar to what we had before with some kind of faded state, which I think we actually have which is is pending property on the thing as well, but we'll kind of see it now if we go in there. And I'm just going to turn off my throttling, all right, refresh this, and then we'll turn it on, and then hopefully, unless I embarrass myself by, I uncommented all the right lines, right?
[00:19:36]
Everyone saw me do it. No one's like, I don't know what you did. We'll find out together. Optimism. Yay. And you can see it adds in there, we've got that kind of saving and sending, where we kind of show something immediately and when it resolves, we get the real one in there as well, right? So like, yes, I had to wrap it in transition and I have to have effectively that optimistic layer over my realistic state, right?
[00:20:07]
But again, these are like, there is some, you know, new APIs available, but the actual heavy lifting of a lot of the code is at the same time also incredibly powerful for creating these abstractions that give the, network request wasn't any faster. Right? The experience for the user felt responsive, right? And I think that is the kind of important part because again, we kind of ended up in that pending state and we're able to apply it as we went through.
[00:20:39]
And again, we are on a very slow internet. If anyone watched me debugging earlier, I had to keep turning this off because even in that intentionally throttle state, it doesn't even care that it's coming from localhost, right? It's like, it is simulating a slow internet. So we'll do it one more time just to kind of show. And we'll watch hopefully as if you peek down here, I think I can actually, let's just look for, can I just do post in here, does that work?
[00:21:17]
I've always looked at the endpoint, we can always do for the post endpoint too. Oh, you know what's funny, if you are making a fake social network and your verb is post and you're looking for post requests, you'll get both anyway. So, needless to say, we'll paste that in there, we'll got that and we'll say. That you can see that it's still pending.
[00:21:38]
Oh, do we not, we gotta turn back on the throttling. So excited. Waiting, waiting, waiting, it's there. And then when it resolves, we get the real one, right? Again, lightweight abstractions that give us a lot of performance, or at least sometimes it gives us performance. Sometimes it gives us the illusion of performance. Sometimes performance is just doing the important things first.
[00:22:04]
Right. And a lot of these new features inside of React are very, again, the hardest and most tedious stuff was the pushing state down, which has been the bread and butter of optimizing React app for a very, very, very long time. A lot of these new features under the hood are easy to forget about, easy to not pay attention as new releases come out, but give us some powerful abstractions that are not always the things that you're going to use every single day.
[00:22:34]
Like, making everything optimistic is technically could be somewhat problematic as well. But, you know, or putting everything, you know, in a low priority queue, you know, like you're not going to need that every time. It's when you have those giant bottlenecks where it's like, yo, there's one function that is causing all of the pain. What's wild is those used to be the hairiest and squirrelliest and hardest to solve performance problems, right?
[00:00:00]
But because of the fiber architecture, we're actually able to kind of do a lot of these things way easier, right? And they feel a lot simpler now, but part of it is understanding the paradigms under the hood, right, that it isn't the way that it has always been in React, right? There are new mechanisms that give us some of these new features that seem easy because of a lot of stuff under the hood.
Learn Straight from the Experts Who Shape the Modern Web
- 250+In-depth Courses
- Industry Leading Experts
- 24Learning Paths
- Live Interactive Workshops