Let's work through some common mistakes when using React's useEffect, useCallback and useMemo hooks (or not using them), and also their tricky dependency arrays. We will even come up with a helpful list of rules to live by.
PUBLICATINO PERMISSIONS: Jack Herrington provided Coding Tech with the permission to republish this video. Original video: https://www.youtube.com/watch?v=lStfMBiWROQ
Check out Jack's YouTube channel: https://www.youtube.com/channel/UC6vRUjYqDuoUsYsku86Lrsw
The video you're about to see was originally posted on the Blue Collar Coder YouTube channel. So after you watch it, if you like it, be sure to jump over to that channel, which is linked to in the description above. For more videos. Just like this one in this video, we're going to take a look at some common mistakes that people make when they use react, use effect as well as use callback and use memo and those pesky little dependency arrays. We're gonna come up with a bunch of rules that you can live by that are gonna make your life much, much easier. But before we get into that, if you like this video, be sure to hit that like button. If you like the video, be sure to hit the subscribe button and subscribe to the channel. Ring that Bell and you'll be notified any time a new one of these videos comes out. We've also got a discord channel that you can jump on. And that's got a great little interview channel that you can jump on and have a talk about any kind of interview questions. Really cool. Check it out. And if you like all that, be sure to buy me a coffee without further Ado. Let's jump in in the office and get use effect, use call back, use memo and those dependency rays all figured out. Alright, welcome to the new office and we're going to start looking at some viewer code here. So this is an interesting little component and it's got some problems. So the intent of this component is that we're going to have an array of numbers and then we're going to fetch those numbers from a JSON payload. So let's go to look at that. It's got one, two, three in it, and then we're going to go and display those numbers. So take a look and see if that actually works. And right now it gives us back an array. And so there's basically three problems here. The first is that they're using state in React in an odd way. Right? They're basically going in setting the value as opposed to using the value setter. And then the second is that they're using fetch right in the middle of a component definition, which means that every time this component is going to be rendered, it's actually going to rerun fetch. And then the third is that there's. I think an assumption here that this is sequential. Right? We set the numbers, we go and get the fetch complete, and then we render it. And that's just not the way that asynchronous code works. So let's go put in a few console logs here and start seeing if we can figure out what some of the behavior is and learn from that. The mechanics of basic state management in React but also use effect and dependency arrays and also use callback and eventually use memo and just get a sense of solid underpinning of how all this sort of stuff works. So let's start off with a console log. Here. We'll say that this is the start of my component, and then we'll put the state so that will tell us what the current value of state is. And then we'll say that we are starting to render. So let's go and just copy that one from the top. And finally we'll capture the output here as a variable and then return it so you can put a little console at the bottom there on the way out. And that will tell us when we're done. Okay. Let's go over here to our Inspector and we can see. Okay. So we get started. We get the numbers. Well, in this case, let's go and make that number is zero. So we just see the numbers which starts off as an empty array, and then we get the render and then we get the finished. But what's happening here is that fetch here is asynchronous. So the control flow is like this. It goes here, it starts the fetch and then it renders, and then it goes out. And that's why it starts with an empty array. And you don't actually get one, two, three by the time this gets done. If you don't believe me, let's go take a look here. Let's go and make this a function that has a body in it. So now put the data coming back and also that we finished our request and clear this out and run it again. And you can see now the entire component rendering is finished by the time that we get the results back from that JSON fetch. Right. So that's why we're not getting one, two, three in here. If it was sequential, then we'd have something along the lines of well, I just go and set this t0456 like that. And yeah. Okay. So we know that we're looking at the right data, but we're not actually setting it properly. So I think the first thing we want to fix is the setting. So we want to go back to a more conventional use of us state. And that means to use break out this array like this and then do set numbers in there. Now this is how you state works, right? It gives us back the current value, and that's a a constant, right. You should never change this. And then it gives you back a setter. And what the setter does is when you set the new value, it immediately tells react to go out and re render the component. So what's our first big learning here when we get to this to make a bunch of rules? Rule number one is to always use the center for you state. So I always want to use this. We never want to set that value directly. So let's go and change out. So we're no longer using the zero th item there. And instead we're going to do set numbers and this is quickly going to roll into our second problem that we have, which is that we're not using Use effect, and we got fetch right in there as part of the render method, essentially of this component. So we'll hit save and immediately in my browser is going to start having issues you can see down here. It's just continuously running and running and running and running it. So why is that happening? Well, so what's happening is the render starts, and then during that we call that fetch, that fetch, then sets the numbers and that forces a rerender. Right. So we're getting the right data a something of a fix. But now we're just continuously calling that JSON over and over and over again. In fact, it's overwhelming my machine to the point where Chrome is no longer really working. If I go down here and inspect, let's see if I can even get up the Inspector. Yes, I can, but yeah. Okay. So there you go. You're starting to see just tons and tons and tons. It's basically an infinite loop of fetches. So how are we going to fix this? Well, there's really two hooks within which you should go and set state. In your functional component. There is Use effect, and there is use callback. Use effect is you use when you want to change state based on either being loaded or unloaded, or if some other piece of state changes and you want to subsequently set another piece of state, and then they use callback, which you use when you have a user interaction like a click, and then you want to set state based on that. So in this case because we're looking for something where we are going to set state based on being loaded or unloaded, we want to use Use effect. So let's go and bring that in. Now Use Effect takes a function and within that you but whatever your state setting function is. So in this case that's the fetch and eventually will go in set that state. Now we'll just use that right. Like that. And now go back into here and we'll see if we've solved the problem. It's not even bringing up the Inspector at this point. So let's try it again. All right. Now, unfortunately, it looks like we're getting exactly the same effect again. And that's predictable because Use Effect in this case is being run every single time this component is rendered. And the reason is that we've provided no dependency array. So the dependency array is the second argument here, and it basically tells react, when should I go and run this function here? And if you don't provide anything, then it gets run on every single render, which is clearly not what we want. So that's a common mistake. So let's put that in there. Always put a dependency array on Use effect for sure. Actually, I'll just add that use callback and use memo. They all have it at least have to be there. Now what goes in it? A lot of people make another common error here, which is to say this fetch is setting numbers, and therefore it depends on numbers. And so they put in numbers in here. And the result is unfortunately exactly the same thing. So we're trying to improve this, but we're ending up in exactly the same infinite loop again and again and again. And it's very, very frustrating. And the reason this is happening is that regardless of what numbers is, we're still going in fetching numbers again, even if numbers is now set to something that we like, we are still fetching. So one way to fix this would be to say, okay, well, if we start off with a count of zero, which we know would be the initial state, that's the only point at which we should then fetch. Just drop that in their statement like that. And now let's see, I'm probably not start another tab. And finally a this works. So we start rendering our component. We have array that to nothing. We then render it. We finish. In the meantime, we've started that fetch. That fetch comes back to the data, it then re renders based on that new data. And finally does the request finished. I think what's a little surprising here is that all of that rendering happens when you set to do that set, which I think is actually pretty cool and fast. So another way to do this, and I think it's a cleaner way to do this is to leave this dependency array blank. And that's because we know that we only want to make this request on the initial render of the component. We only want to do this once. And so what happens is use effect basically starts off with this dependency array being internally undefined, and then it does a comparison to whatever you provide, which is an empty array. But then the next time around it compares it to that empty array again and says, Well, there's no difference. So you get run once and exactly only once. So I'm still getting a little warning here. And that's because at this point it's telling me, well, you still are looking at numbers at length. Let's get rid of that. Save it. And now there you go. I think that's actually the cleanest version of this code. Alright. So let's capture that was one more rule to run. Use Effect or any of these others only once use an empty array. In addition to that, another good rule is to say, don't depend on data you set. No, that's not a hard and fast rule, as you saw with my comparison numbers at length. Yes, you can do that. But you got to be really worry about that and make sure that you're doing it the right way. Okay, now there's two more hooks that use this dependency. Right. And it's worth jumping into those and seeing how they work as well. So you can avoid any common pitfalls with those as well. Before you get in there. Let's go in and remove a bunch of these console logs. Kind of clean this up a little bit so we can have a much more tourist piece of code. Alright. Looks pretty good. So let's talk about how to go and handle setting state based on user interaction. So let's go and create and add one button that goes and adds another element to this array and see how we do that now in videos in the past, what I've done is create a function right here and then use something like set numbers. Given a new array with the current numbers and then maybe the length of the array plus one. And I'll set that to the on click handler. See? Alright, that works pretty well. So in this example this is probably fine. But if you're going to go and build your own react hooks or if you're going to go and send this add one call back down to any subcomponents that kind of thing, then it's actually really advantageous to use use callback. So let's go bring that in and use callback works a lot like use effect. The first parameter is a function and then the second parameter is a dependency. Right? So at the moment we're just going to set that dependency array to nothing seem to work with user fact. I wonder what it's going to do here. So let's do save and then add one. That's kind of interesting. So why is that happening? We started with one, two, three. We did add one, then we got one. So what's happening here is kind of a combination of JavaScript closures and also the fact that this dependency array is set to nothing. So what happens is the first time that my component gets rendered. The array is an empty array, and then we create this add one call back and it goes in here and say, okay, I'm going to capture the state of numbers, which is an empty array. And when I do set numbers, I'm going take that empty array and then add the next one. That is the numbers at link, which is zero plus one. So you get that one. That's how you get the the Ray with a single value of one. So how do we fix this? Well, what we can do is we can regenerate the add one callback. Anytime numbers has changed. And the way that we do that is we just add numbers to that dependency right now. Anytime numbers takes a new value, we will get a new version of add one. So we'll save that. And now it works because the callback gets regenerated every time. Now that callback has a stored value of 123-5678 and I want to hit add one now is adding one correctly to that. That works another variant of this. If you don't want to go and keep creating new versions of Ad one. Every time numbers changes, you can use a a different variant on how to do set numbers. So let me get rid of that for a second. And set numbers in this case can take one of two different options. It can either take a value like in this case a new array or it can take a function and that function gets given the current value and is expected to return from that a new value. So we could do current value or current numbers and then return a new array based on current numbers. And so now this add one is good forever. It takes any current state and mutates it. Let's try that out and it works is fine. So there's two different variations for use. Callback for you in two different ways to manage that dependency. So the last one I want to look at is use memo. So let's create a little thing here called sum. We're going to sum up all the numbers and to do that, we're going to reduce the array. So say that number is not reduced and that takes a function. That function takes an accumulator and a new value, and we're just going to add the accumulator and the value and that will return a new value for the accumulator and we'll start the accumulator at zero and that will give us our sum. So let's go in. I'll put the sum there and away you go. Now, just like with used call back. This is actually probably fine. In this case, this is not a particularly expensive routine. But if you had something that was expensive but synchronous, you would use memo. And what Use Memo allows you to do is take that expensive process and say only run that when this state changes. So let's try this out. So again we're going to wrap this in hook use memo and we use an empty dependency array and we'll see what happens. So now we got some at zero. Okay. So why is that? Well, again, we start off with an initial state of an empty array. So it comes down here to use memo for on the first render and it says, Great, I'm only going to ever run this once and I'm going to do this reducing thing right now. It does it on the array. The sum of an array is zero and away you go. So now it's zero and it's going to be zero forever. So again, the way that we fix this, we say, well, this one depends on numbers. So we got to put that in there. And this leads us to our final rule, which is always add all the state you read from to the dependency. Ray, let's go see what works. Hey, perfect. Nice. Alright. So there you go the three dependency based hooks and also some common mistakes around them and also some rules that will help you avoid those mistakes in the future. Well, I hope you've learned a lot about dependency rays. Use effect, use callback and use memo. And you've gotten some cool rules to live by. But if you've got some rules to live by, I would love to hear them. Be sure to put them in the comment section down below. Of course. In the meantime, from me to you, be happy, be healthy and be safe.