This course has been updated! We now recommend you take the Intermediate React, v5 course.
Transcript from the "useMemo" Lesson
>> Now we're gonna get into, you may never have to do use these kind of hooks, right? Everything that I showed you up until now, there's a likely chance that you're gonna use at some point in your React career, if you end up writing enough React. The ones after this, 50-50 at best that you'll ever have to use these.
[00:00:23] The next two, useMemo and useCallback, one, are more or less the same thing with a slightly different API. And two, specifically, they are performance optimizations. So let's get into useMemo. I have to look these up, by the way, these ones from here on out, I do not use them enough that I can remember them.
[00:00:45] So I have to look them up every single time, which is why I have my notes here. Okay, so memo. So here is a performance problem for you. Here we're gonna talk a little bit about Fibonacci sequences. But we're doing it because explicitly they're extremely computationally expensive, right?
[00:01:14] So you can replace your Fibonacci with SuperExpensiveThingToDo, right? This could be like crypto hashes or something like that, something that's just gonna require a lot of computational power. In this case, I made an intentionally bad Fibonacci sequence. Please render, thank you. I made an intentionally bad Fibonacci sequence so that it takes forever to process.
[00:01:46] And also it's just doing excessive recursion, is what this does. In fact, that would be a better thing to call, this function is excessive recursion, okay? So I have two things here happening. I have my state of what the number is here. So the Fibonacci of 1 is 1, if I click 1, Fibonacci is 2 of 2.
[00:02:09] And notice that at first this is pretty good, but then once we start getting into bigger and bigger numbers, this will start getting very slow. Once we're getting into the 40s, yeah, here we go. So notice if I click this, it takes a while to update, right? And why is that?
[00:02:33] It's literally adding one to itself this many times, that's what Fibonacci does. So that takes a while. But we have a problem here, is that the user may want to use this other very cool, red-green clicker here while this is computing. So how would you handle that? Well, we can use something called useMemo, To kind of help along with that.
[00:03:06] So if I click + here, notice that I can't click on this right now. But notice that if I do click on this right now, it does work, right? So why is it working that way? Well, useMemo is helping me along here. If I did not do that, if I did this instead, const fib = fibonacci(num), like this.
[00:03:42] And let's just have the start at 25 right now. So if I kept this up until, I don't know, 30 or something like that. Okay, so now it's in a state where now it's expensive to call Fibonacci. If I click on useMemo, notice how long it takes me to change over.
[00:04:10] Right, because what is it doing here? It's recalculating it every single time that it renders. That's a problem, right? Cuz I want both of these things to stay responsive. So that's what useMemo is here. You can use it to say, hey, I do need you to run this, right?
[00:04:27] I do need to know what the Fibonacci of 38 is right now, right? But I also need users to be able to click on my red-green clicker here. Does that make sense? It's a very niche use case. So one, hopefully you're not calculating the Fibonacci on every re-render, right?
[00:04:48] But you can imagine probably cases where I have to go do something and it's expensive, right? I have to go run this web crypto hash here, but I don't wanna hold up the rest of my React app. And then I don't have to do it very often. I only have to recalculate this only when the user increments the number.
[00:05:05] It's gonna be the same number every single time after that, right? The Fibonacci of 38 is always going to be the same thing. Therefore, I can say, hey, calculate this, but then keep it as long as number doesn't change here. This number array works just like useEffect, it's like hey, only recalculate this when num changes.
[00:05:25] So let's make this again. 40, right? So this will take forever to re-render. But now, notice I can click on this as much as I want and it goes back and forth, no problem. Okay, so, first thing, I've shown you a very powerful performance tool, do not go and use and abuse this everywhere.
[00:05:51] Because, what happens if later I start expecting this to re-render? It's like, why is the Fibonacci not re-rendering, right? This makes no sense. The state's updating, things are happening, why is that not re-rendering? And they have to go in and track down as like, this person used useMemo in here, that's why this is not re-rendering.
[00:06:09] You run into this new class of bugs of why is my state not changing when I expect it to? It's not obvious, right? So again, with performance problems, I've kind of reiterated this several times throughout all the courses that I've taught here, is have a performance problem first before you solve a performance problem.
[00:06:30] Because almost always you will solve the wrong problem. You're thinking this is definitely gonna be a problem. And one, it wasn't ever going to be a problem. And two, you created a new problem for yourself. So don't do that. So that's my advice for useMemo is, only use useMemo when you actually have a performance problem that you're explicitly solving at the moment.
>> So, I'm trying to understand it really well. So to state in pure English what useMemo is doing, is it's kind of saying like you said, is it accurate to say we're maybe adding a hook or something? Or using that to say, don't render this unless the dependency array, or some value in the dependency arrays change?
>> Yeah, let me tweak your phrase in there, is don't recalculate this.
>> Don't recalculate.
>> Don't run this function, right? You're giving it a function of, if you need to recalculate fib here because num changed, run this, right? Otherwise, just use what it was last time, right?
[00:07:42] Cuz again, num didn't change between my click here and here, right? But isGreen did, and every time isGreen changes, it re-renders the entire component, right? But because num didn't change, you just say, don't re-render this, just leave it as is, it's fine. Unlike if we were being quite silly, we could put isStillGreen, I don't know, something like that.
[00:08:11] And then this could be isGreen, and this could be dependent on isGreen. And then here down here we would do isStillGreen. Now, why the hell would you do that? You wouldn't, this is objectively a horrible idea, right? But I just wanted to show you that you can memoize anything, right?
[00:08:37] This is just a function that's going to return something, right? But by wrapping this in a function instead of just putting fibonacci here, If you do this, it's gonna calculate it, right? Because you're actively invoking that function. If you put this inside of a function, this isn't gonna get calculated until this little function that we created gets run.
[00:09:01] So with useMemo, you're just providing it a function of, hey, anytime num gets changed, run this, and that'll be the value of fib. I don't know, does that help clarify a little bit? Okay, cool. Don't do this, that's upsetting. I don't know why I did that.
>> Well, ternary computational power.
>> [LAUGH] I saw a code base that someone was working on, and it's like they memoized everything. They actually wrote a ESLint rule that's like, you must memoize all of your state. No, don't, React is really fast, even old devices handle this just fine. There's no reason to make your life so much harder.
>> Actually, I have one further understanding statement.
>> Is it essentially that useMemo, then, like you said, we don't recalculate these values? Is that then essentially the code level, not the DOM? We're not talking about render, we're talking about in our code. We're not gonna update these values, because then updating a value would trigger a refresh of the DOM, a rerender of the DOM, because something had changed.
>> Well, basically, this MemoComponent, it's gonna have to re-render a bunch, right? Because, especially if this is a popular thing for people to click on, this useMemo example, every time I click on this, this function gets run, 100% of times. And then sometimes, even still, even when I'm not clicking on it, you don't know when React is going to choose to rerender your components, right?
[00:10:33] So this function is gonna get run a lot. And basically, it's saying, I know this is gonna have to get run a lot. But as long as num is not changing, do not calculate this again. Just use what it used to be, because I know what it used to be.
[00:10:48] Reliably, the Fibonacci of 40 is always going to be this, right? So despite the fact that MemoComponent function is running frequently, don't calculate this unless this. Don't run this function, use what it used to be, so that you don't have to.
>> Okay, and so maybe that's why you brought up the pure state of this.
[00:11:08] The fact that given to the same inputs, you always have the same output. If there were for some reason, some dependent input, then this wouldn't be valid. Because maybe useMemo wouldn't know that something had changed when it should have, or something like that.
>> You would just put that in here, right?
[00:11:27] So you can have multiple things in here.
>> Okay, cuz of your dependency, right, yeah.
>> Cool, thank you.
>> Yep, good questions, yeah, Mark.
>> When would we use this instead of the other pattern you showed, which is useEffect dependency array when something changes?
[00:11:49] So useEffect is for causing side effects, so this is a good question. useEffect is for side effects, so that's like requesting something from an API. It's basically like render my function, and then go find something for me and bring it back, and then let's rerender it again. Or posting to an API, maybe you're doing your analytics there.
[00:12:10] Or you're reaching out to something else, whether that's a browser API, an API, something like that, right? For that, you provide it with an array of dependencies of this is when you would need to do this again, right? The user committed some sort of action, so call my telemetry again.
[00:12:27] Or the user typed something into my search bar, run typeahead again, right? So it's basically saying, I'm causing effects based on something else that's happening. And then you're providing it with when do I wanna do this again? That's useEffect in a nutshell. useMemo is like, I have some data that's computationally extremely expensive for me to acquire.
[00:12:52] Basically, I know what the number is, but turning number into the Fibonacci answer, I know is extremely expensive. And I only wanna do it when I have to. So in other words, useMemo is purely for performance, 100%, it's just performance optimization. You don't use it for anything else ever.
[00:13:16] Yeah, I feel okay in saying that. Don't use it for anything else ever, otherwise you're abusing the tool. useEffect, it's much more generally applicable, it's like I have something that I need to do outside of my normal rendering. You typically wouldn't use useEffect for performance optimizations. I think that's fair to say.
[00:13:42] I'll have to think more about that one. Yeah, you have a question?
>> This explanation may not make sense, but it helps in my brain. useMemo has to exist because we're using functional components which have closure in the call stack and stuff. And you're kind of forcing it to do something it wouldn't.
[00:13:57] Like a function would redefine a variable every time, and we're saying, please, don't do that.
>> Yeah, I think that's fair to say. You're trying to move something out of the pop path here, basically. Okay. I think that's fair. Let's go on to useCallback, right, yeah.
>> I think you covered this yesterday, but why are the hooks always placed on the top of the function?
>> They don't have to be, but it's a really good idea. For me, it's just habit, so that I look at the top of the function component, and I can see here's all the hooks that Brian's currently using. But the only thing that you have to, is an absolute must, is you gotta keep your hooks out of if statements and for loops.
[00:14:53] Or while loops or anything that has some sort of variable amount of times that it might be called. By putting in an if statement, it might only be called one through zero, right? Which is bad, or if I call it in a for loop, it might be called ten times, it might be called five times, right?
[00:15:13] Now, I've seen people do dumb things like, well, I know if I do for let i = 0, i is less than 10, right? i++, and they try and get cute with this and make an object or an array of these. Just don't, just put ten lines of code.
[00:15:36] I don't know, if you were making 100 hooks at the same time, I don't know, just make one hook that that's an array, right? Instead of trying to make 100, yeah, anyway, it's dumb. I feel okay saying never put hooks in if statements, never put hooks in array or in loops of any kind.
[00:15:59] There's a different way you could rethink the problem that would lead to using one hook instead of ten hooks. Okay, any other questions, yeah.
>> I was gonna say I've seen people, too, use objects. Like if you have ten useStates, then you just switch to an object.
>> Yeah, I'm like use reducer or something like that, yeah, totally.