Transcript
00:00 The latest ref pattern. Here's the one-liner for the latest ref pattern. The latest ref pattern allows you to have a reference to the latest value of a prop state or callback without needing to list it in a dependency array when accessing it in UseEffect. And some of you are hearing this and you're like, oh my goodness, why don't we just use the latest
00:18 ref pattern for everything? Because I hate the dependency array in UseEffect and use callback and use memo. I hate it so much. So why don't we just latest ref everything? So let's talk about that. I actually have a blog post that goes into this in much deeper detail than what we're going
00:35 to be covering today that is titled How React Uses Closures to Avoid Bugs. So you can read up on that a little bit. And then also I have a blog post that you can take a look at about the latest
00:51 ref pattern itself. So feel free to read through both of those to go a little deeper than we will just now. But I'll give you a high level overview of what this is all about. So years ago we had
01:06 class components. You'd say class, here's my component name, extends React.component, and React.component is what turns this thing to a component. Gives it a bunch of things like this.set state and stuff like that. And you have your lifecycle hooks and all of that stuff. They were
01:22 called lifecycle hooks, but they're nothing like the hooks that we know today. So here we have our pet feeder. It has state. So we have selected pet food and we have this pet food chooser that allows
01:34 us to change that. And then we have our feed pet function that when this button is clicked, we'll say we're feeding Pikachu or something. And then what we do in our feed pet function is we have to
01:53 call this can eat property on the pet. So we get our pet as the props. We determine can this pet eat? Here's my selected food. And then if they can eat that, this is asynchronous for some reason, maybe we're making a network request or something. If they can eat it, then we will go and tell the
02:12 pet to eat the selected food. So there's a pretty big problem in this code, and it is just by the nature of the mutability of a class and instance properties. And the problem is that somewhere
02:27 between here, between the await and the promise that's returned here, some at some point during that promise being in flight, we could actually change the selected pet food, or we could change
02:42 the pet, even the props for the pet could change. And so we end up with this can eat variable that's based on one pet and selected pet food combination. And then we're using that can eat Boolean
02:58 and to feed a pet that could potentially be a different pet food combination. And that could be pretty bad. Because the props can be mutated, and not by us, but by our parent component re renders
03:14 and react will update this dot props. And then also the state could be updated, like we changed our selection. So this is this was the default behavior in react with classes. You can solve
03:28 this problem pretty easily. But people rarely did I made it a habit of just doing this at the top of all of my functions, just pluck off the things that I need from prompts and state. And then I no longer worry about that, because I'm going to have access to what the pet and the selected food pet
03:43 was at the time of this function call. So even if there is something that happens between here, I still have access to the original patent selected pet food. And there's also like you got to think about cancellation retries and stuff, we're not getting that far into it.
04:01 But that's the that was the default behavior of class components. When we switch to function components with hooks, things kind of changed a little bit. So this would be how you refactor this to a hook component, you've got your state here, you have your feed pet function, which gets
04:18 called on click, you have your pet food chooser, and you're allowed to update the state all of that. And so when this is clicked, you're going to say, hey, I've got a pet, it's referencing reference from those props. And it's not just because I'm destructuring either, if I said
04:33 props right here, and then props dot pet dot can eat, this wouldn't change at all, because the props object is not going to be mutated, it's not changed, we no longer have an instance that we're mutating or changing properties on this object will be the same forever and into eternity. And
04:52 if pet feeder ever gets rerendered, because it's props change, it's going to be a different object of props. So we don't have to worry about the props changing underneath us. And then the selected pet food is also it's a constant declaration. So it can't be reassigned anyway. Technically, it can be
05:10 if it's an object, it can have properties that are mutated. And that's why some people get all huffy about const. But the assignments of this variable cannot be changed. And as far as I can see, there's it's not an object that we mutate at all. So this problem completely goes away,
05:26 because now you don't have access to anything that can be mutated in such a way to cause that bug that we saw. That said, maybe there's a scenario where you actually want the previous behavior.
05:41 And so if you wanted to do that, in our case, we probably don't. But there are definitely situations where you're like, I actually want to just be able to get the latest version of everything all the time. So if we wanted to have that previous default behavior, we can use a ref
05:58 because a ref allows us to mutate a value at any time without triggering renders or anything. And at any time, you can get the latest or the most current value or version of that value. So here, we're getting the latest pet ref, and we get the latest selected pet ref. And inside of a use
06:15 effect, we're going to update those. Now, why is the use effect necessary? Because components can be with with use deferred value and transitions and set optimistic, all of those components can get re rendered like a bunch of times and those renders get thrown away. And so all of these
06:34 concurrent features that react has can actually cause our functions to run over and over again. And so if we were to move this out here, then we're reassigning the ref to values that may actually never come to fruition. So it can be actually pretty serious, as far as the bug is
06:50 concerned, and very difficult to reproduce those bugs. And so this is why it's very important that your functions, your components are item potent, which means they can be called many, many times and it the it won't impact the result. So we put it in a use effect because the use effect
07:09 is run only when the component has finished rendering and it's been committed to the screen. So inside of that use effect, we're updating our refs so that we always have the latest value of our pet and our selected pet food. We don't technically need to have a dependency, right?
07:29 If you add one, then you want to put the pet and selected pet food in here. Technically, that is, I don't even know if that's more efficient, because now react has to compare the previous array with the new array, and assigning a thing to the its current value the
07:47 same. Like if, if the pet didn't change, and we're assigning the latest pet ref current to the pet, again, I don't think that's going to be a performance problem. So I pretty much don't use a dependency array for my latest ref pattern here. But that's what we're looking at this latest
08:02 ref. Okay, so we're trying to reproduce the class component default where things could be mutated. So we're going to update our feed pet to say, hey, latest pet ref, get the current, that's our current pet, we're going to see can that pet eat the current food. And then we get the can eat.
08:22 And then we're going to say, hey, current pet, I want you to eat this current food. And so we do reintroduce that potential problem. But we really only want to do that when it's a desirable behavior. So this is why it's important for you to understand this before you get too far into
08:38 implementing the latest ref pattern, because it, it's not just like, oh, sweet, what now I don't need to put this in a dependency array, because I can always just get the latest version. Like it's not just a get rid of dependency array problem solution situation. It is a semantically
08:55 different scenario that can be really useful at times. And so the the entire pattern can kind of be summed up in this simple use effect right here. So here's here's an example,
09:12 where we're trying to get a callback that is called instead of a use effect. But the problem is that whoever is using this use example one, they would have to memoize their callback. And maybe that's okay, maybe it's not. But we we definitely have to put that callback in the array
09:33 in the dependency array. And if that callback is not memoized properly with use callback, then we're going to be triggering this use effect every single render. And that's probably not what you want. Whereas here, you can use the latest ref pattern. And now you don't have to include the latest or the callback in this dependency array,
09:53 because it's a ref and you don't include refs in dependency arrays. And I actually have a blog post about that also. But that is that's another example of this pattern being used for callbacks. And this is actually what react query does to allow you to pass a callback
10:10 for whatever query you want to implement. And it handles recalling those callbacks based on the key that you provide. And so yeah, you do have to think about like, okay, so when do we get an
10:24 updated version of the callback? So you do need to think about that. But this is a pretty solid approach to simplifying an API as long as you're okay with the trade offs that you're going to get.
10:39 Okay, I think that I have talked enough about the latest ref pattern. So let's get into the exercise and actually do it.