React Performance, v2

Memoization with useMemo

Steve Kinney
Temporal
React Performance, v2

Lesson Description

The "Memoization with useMemo" 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 introduces a complex application called Memomania, showcasing high-level opportunities for optimization. He demonstrates using the profiler to analyze changes and explains how useMemo and memoization can improve performance and prevent unnecessary re-renders.

Preview

Transcript from the "Memoization with useMemo" Lesson

[00:00:00]
>> Steve Kinney: So we have, if you're like, actually I've never used any of these, we can kind of play with it a little bit. We have an example that I fired up. It's called, I think it's Memomania. Let me just recheck that. Yes, I'm good at naming things. Memomania, where we have an application that I'll kind of show you and we'll look at some high level opportunities and like triage it. I will leave it as a little bit as an exercise to the reader because I have, for all of these, if you do try to explore one that we don't cover directly, there is a step-by-step guide as well, kind of for you, so we can kind of look at some of the high level concepts where it's interesting to us for our purposes.

[00:00:42]
We don't necessarily have to be complete when we hit that point of diminishing returns, but I do want to kind of look a little bit like how to triage, how to see, I think those, these are the important parts because I think like some of the other, like the very basics of how to use it we kind of saw earlier, and I think it gets covered a lot as well, but the kind of nuance of when is it, how do you tell if it's worth your time?

[00:01:04]
Sometimes it's obvious, but sometimes it's interesting to see the tools. So here we've got what seeks to be a somewhat expensive set of operations where we can do like a Fibonacci sequence on a given number or factorials or so on and so forth, and we of course have the problem that we have had multiple times now where changing anything changes lots of everything. And the goal here is we can solve a lot of this with some of the memoization pieces.

[00:01:34]
But what I want to do, and well, we can kind of look at one or two opportunities, but what I want to do is also just look at like what this starts to look like in the tools, right? Because like, you know, like when you go to an app of any age, you'll see lots of stuff flashing and you're going to be like, well, I just watched this course. I'm going to like be the hero of the day. It's a little trickier than that, but it's not necessarily tricky, but it's like there's some discipline to it.

[00:02:03]
So we've got the profiler and we can go ahead and I'm going to clear that. We'll record, or maybe I had it running the entire time. Whoopsie. Delete, we'll make a new one, we'll add a random calculation, right? Everything is flashing, so on and so forth. And we can see in the, I got a, that's not us, by the way, that is some Chrome extension that I need to track down at some point or another. My apologies.

[00:02:34]
But we go back into the profiler, we can kind of see what, why things changed, right? And honestly, if it's not like an issue where like the parent changed or just the parent changes and it's not any other like props changed, then we know that we have an opportunity here to like go ahead and be able to like figure out what the various pieces are, right? So, again, we'll go in here, we'll like up the number and we can see lots of things changing, right, and some of these happen incredibly quickly as well.

[00:03:09]
We've got the card, calculation card, we've got the long one here that we actually changed and did the expensive operation. Cool, and so we can begin to start to figure out like what are the props that changed. Well, interesting, like the calculation, sure, but on update and on delete and like on update and on delete are what changed over here and yeah, this one, you know, this whole thing is 231 milliseconds, right, which is definitely like, actually this one was only 3.1 of the entire time we were rendering, but like again, like tiny little app.

[00:03:48]
And we can begin to start to look at like some opportunities to kind of like play around with it a little bit as well. So, we can go in and there are a bunch of opportunities because it's intentionally built for that, which is the calculate function, as you can tell, is expensive, right? And right now it runs on every card render, right, in this case. So like for instance, right, we can see all of them here as well.

[00:04:28]
These are each state change, right? So like, let's go ahead and let's do it for different ones, right? We'll go ahead, we will clear this, we'll record it. Grab this one, we'll grab this one, we'll add a random calculation down at the bottom, we'll delete one, right, and so we've got kind of all of these different cycles where, you know, card calculation keep one keeps changing, which seems weird to me.

[00:05:00]
So let's go ahead and like let's look at some of the code. Cool. So like, one of the things that we can do is we can go to this one's calculation card, right, where it is going to calculate, you know, the type and the input. Now, ideally, if, you know, this is math, math is by definition where like this idea of functions and functional programming comes from, right? Like lambda calculus, right, we could say that, hey, if it is true that like the type of calculation we're doing and the input hasn't changed, we should get the same numbers out, right?

[00:05:41]
And so, but here's the tasting note, which is, it would be very tempting to toss in calculation, right? But the calculation object with all of its different properties is, could be a new object in memory every time. What we care about are the actual like primitive values on the other end. Calculation type is a string, calculation input is a number, right? Those are the primitives. So whenever you're trying to use a useMemo on those inputs, like, it is worth considering, am I working with a primitive?

[00:06:13]
Great. Am I working with a full object? Well, then I need to like, in my mind go like, am I getting a new object even if they feel the same? I can console log and see that they're the same every time, but that doesn't mean they're actually referentially the same, right? And so in this case, we would care about those primitives, right? Again, like, calculation type is actually just this like union of strings in this case.

[00:06:52]
And so here we could do the useMemo. IntelliSense don't let me down. In here as well, and here we care about those primitives, right? And why are we getting yelled at? Because I got to make that a function. So, the first time around, it will call this anonymous function, which we'll call it with these two arguments. And then, should this string be the same as last time and this input number be the same as last time, it's like, well, I don't need to do this again, right?

[00:07:23]
I can just take the values from last time that I've saved. Now, there's something to call out here too, which is just like, well, I'm wasting time if I don't need it. Like there's always a trade-off with caching that we should be aware of, which is, if doing something is slower than not doing something, storing the like last value of something takes up more memory than not storing the last. You are trading render speed for a certain amount of memory.

[00:08:03]
This is why this stuff is tricky, right? And so in this case, we can begin to cash it in this case. So now we've got that there. Let's see if we see any immediate differences. I don't know if that was good enough just yet. Oh, it was started by default. Let's record. Right, you can see that we are like that new one was expensive, right? But that first one that we had before that key of one that we saw earlier, like it re-rendered because I got those new update and delete functions that we kind of solved in the last example.

[00:08:47]
But the actual type and input were the same. So it didn't have to run again, right? And it shouldn't have run because all we did below was add new ones. We didn't change the number going into this one. We simply added new ones, and again, if we went ahead and did what we saw last time, which is then to also go ahead and start to like both like memoize one, the functions that we're creating so they're the same every time.

[00:09:20]
And then also wrap the function itself in React.memo, right? Then we would get to the point where, okay, the functions are the same every time and then the inputs are also checking calculation on update and on delete each time. Then we get to the point where we have gone and we have locked down everything manually. And you're like, that sounds stressful to me. Well, one, if it is causing unique pain in your application, you will find the time and effort to do it, but two, yeah, it is, because again, all you are doing are analyzing code and using some heuristics to figure out how to annotate it with doing the right thing.

[00:10:07]
You know what's really good at taking a set of instructions and following heuristics? Computers, super good at that, right? And so if only we could do that at build time instead of having to do it by hand and then run it at runtime, because again, the act of doing this stuff is also somewhat like we are creating, we're wrapping stuff in entirely new functions, adding stuff to the call stack, doing all this stuff, and like, it probably saves us a certain amount of time.

[00:00:00]
Like, that one little change got me down from, I think it was 31 before down to 23, you know, for the entire thing, and that's with adding some, right? Not bad for a line of code.

Learn Straight from the Experts Who Shape the Modern Web

  • 250+
    In-depth Courses
  • Industry Leading Experts
  • 24
    Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now