React Performance, v2

React.memo() & Referential Equality

Steve Kinney
Temporal
React Performance, v2

Lesson Description

The "React.memo() & Referential Equality" 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 memoization and its role in the React compiler. He explains how wrapping components and using hooks like useMemo and useCallback helps React.memo optimize rendering by preventing unnecessary re-renders.

Preview

Transcript from the "React.memo() & Referential Equality" Lesson

[00:00:00]
>> Steve Kinney: In our last endeavor, we touched upon some of the memoization pieces, right? Particularly useCallback, and we saw a little piece of useMemo. These are, we will talk about them if you are unfamiliar with them. I know they get covered a lot, and I kind of want to also talk about them and then their relationship to React compiler. So we'll kind of take a look a little of them, but with a kind of a different take than you might see in a normal React course, which is like we cover them a lot in those kinds of courses.

[00:00:32]
But here we're going to talk about also like how to tell with the profiler, if this is a good thing that is eligible for memoization and stuff along those lines. We'll look a little bit at how to do it, but then I also kind of want to then pull that into React compiler and how those things all relate as well. So let's just do the kind of high level first, which is we've kind of got two hooks and what we would call a higher order component, which is a term of art that I think was a lot more popularly used a few years ago.

[00:01:08]
You do not hear as often, but all it really means is a function that wraps a component. You could argue it's a component that wraps a component. You can argue that components are functions, we can argue all day. But effectively, it is a little fun little shell around your component that checks to see, hey, am I getting the same props as I got last time? If so, then like, I don't, if this is a pure component, then if the same things go in, the same things could come out.

[00:01:45]
Now, I say that for a few reasons. One, if you go around, you're like, if this is so good, why doesn't React do it to everything? One, I'll remind you that checking is a thing. Two, that only matters if you are a pure component. What does that mean by a pure component? Again, the things that go in directly affect the things that come out. It's easier to describe what is not a pure component, anything that has useState or useContext hook in it, right, because those could change on their own, which means now you wrapped this thing in a shell when it could also just render on its own and for other reasons, right?

[00:02:22]
There are, again, but there are some heuristics like why doesn't it just check? Well, because previously, React was only really running at runtime, right? So it has no way to analyze the code, it just has the ability to execute code. You can guess, you know, as I keep mentioning React compiler, that one, that is not always exclusively the case anymore, and two, that there is now a way to analyze the code before running it.

[00:02:51]
This is all true. But like I said, you know, a lot of these get covered a lot. It's worth mentioning them and it's worth mentioning how to like tell it how to relate, but like, kind of just with enough other topics to cover, I don't want to like go down too far down the rabbit hole. We saw useCallback, useMemo, those are hooks, and then this React.memo wraps a function completely, and it does some pretty sophisticated stuff that is relevant to our interests.

[00:03:19]
For instance, obviously, it will check to see if the two objects are the same. And there are some nuances to referential equality in there, right? If you're whipping up a new function or a new object, then like, they don't equal each other, right? Like the classic, it's almost like the things that you do with immutable state to make sure renders happen are the things that would break React.memo, right?

[00:03:50]
Because if you were to make true equals true, one equals one, the word foo equals foo, but like two objects, even though they look mostly the same, are different objects in memory. Two arrays with the same objects are different arrays in memory. Two functions, if you did not use useCallback, are the same objects in memory. So without those hooks that we saw earlier, you could wrap stuff in like React.memo and it's not going to do anything, right?

[00:04:19]
So if you find yourself spreading all of the properties, and any of those, even the ones you don't care about, would break the memoization. If you find yourself passing out things that are not primitives like booleans, strings, numbers, null, but I don't know how much you care about that, but you know, no, and undefined. I think those are all of them. Like symbols, right? You will also not break. So it's like one of those things like unless you're doing a lot of things right, again, useMemo, useCallback as we saw just earlier, you can accidentally break any of the memoization.

[00:04:59]
But it will do a few things again, because we, as we know, doing less stuff is faster than doing more stuff. I think the interesting one is the first thing we'll check to see like, for the object or whatever, does it have the same number of keys? So before it goes and checks, even like, are these referentially the same, if it has a different number of keys, we know that they're different. So again, even some of the heuristics in React's memoization is following the principle of doing less stuff is faster than doing more stuff.

[00:05:34]
And if you can find a way to opt out a little bit early, you can at scale save stuff as well. And, you know, like it will just do that high level check, right? So it's not going to do a deeply nested check on everything. It's just going to check, is this object the same thing in memory as the other one. You're like, whoa, and I saw people early on who were like, I'm going to just deep equality everything all the time.

[00:06:00]
Sure, that will save you some renders, I guess, at the most like one of the more expensive operations you could do, and that's where always the danger of trying to memo at all costs becomes deeply problematic, right? And so these become things that you can use, but as you can see, even if you're starting to feel confused, I'm like, okay, so then I have to wrap the functions that I make and useCallback, and if expensive stuff happens, then I need to useMemo, and then I wrap components in memo, which is different than useMemo.

[00:06:35]
Like, it is confusing. And that is where we kind of will pull into some of the stuff with the React compiler and how that plays in there, because what the React compiler seeks to do is with the ability to statically analyze the code at build time, right, it should be able to at least for the most part apply many of these patterns on your behalf. And this is not a feature exclusive to React, right?

[00:07:03]
Other like kind of later front-end frameworks like Svelte and Solid come to mind as ones that have done this from the get-go, where they will statically analyze the code at build time, figure out what those optimizations are, put them into place, and then remove a lot of the runtime dependencies. And by not having to do a lot of stuff at runtime or being able to do it with like much simpler like code injection concepts, guess what?

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