Pure Components and React.memo

Account Required

This free lesson is available if you sign in.
If you don't have an account, you can sign up for free.

Pure Components and React.memo

Sign in to access this content

You must sign in to access this content.
If you don't have an account you can sign up for free!

React memo function

The React memo function can be used to create a pure component. Pure components do not re-render when the value of the props does not change. This can result in better performing applications.

For example, in the sample below, App will re-render due to a state change (count), but the Lyric components will not re-render as they are pure and their props do not change.

App.tsx
lyrics.ts
import React from "react";
import { useState } from "react";
import styles from "./App.module.css";
import { lyrics } from "./lyrics";

export default function App() {
const [count, setCount] = useState(0);
const inc = () => setCount((count) => count + 1);

return (
<div>
<button className={styles.button} onClick={inc}>
+ {count}
</button>

{lyrics.map((lyric) => (
<Lyric key={lyric.id} value={lyric.value} />
))}
</div>
);
}

// `React.memo` => Lyric will only render if `value` changes
const Lyric = React.memo((props: { value: string }) => {
console.log("rendering");
return <div className={styles.value}>🎵 {props.value}</div>;
});

JavaScript === Recap

React.memo uses === for comparison of different prop values. This is called a shallow comparison.

It means that comparison works reliably for primitives but not for different instances of similar objects.

console.log('hello' === 'hello'); // true

console.log({ value: 'hello' } === { value: 'hello' }); // false

Custom equality function for React.memo

You can pass in a comparison function as a second argument to memo. This comparison function should return true if the previous and current props should be considered equal.

App.tsx
lyrics.ts
import React from "react";
import { useState } from "react";
import styles from "./App.module.css";
import { getLyrics } from "./lyrics";

export default function App() {
const [count, setCount] = useState(0);
const inc = () => setCount((count) => count + 1);
const lyrics = getLyrics();

return (
<div>
<button className={styles.button} onClick={inc}>
+ {count}
</button>
{lyrics.map((lyric) => (
<Lyric key={lyric.id} lyric={lyric} />
))}
</div>
);
}

type LyricProps = {
lyric: { id: string; value: string };
};

const Lyric = React.memo(
(props: LyricProps) => {
console.log("rendering");
return <div className={styles.value}>🎵 {props.lyric.value}</div>;
},
/**
* Custom comparison function.
* Return `true` if props should be considered equal
**/
(prev, next) => {
return prev.lyric.id === next.lyric.id;
}
);
javascript
typescript
react
playwright

Enjoy free content straight from your inbox 💌

No spam, unsubscribe at any time.

Transcript

00:00

Reactor provides a neat little function called memo, which can be used to improve the performance of your replication. And in this lesson we will look at the problem that it solves along with this various features. So let's go to demonstrate the problem that the memo function is designed to solve. Consider a simple custom component that takes a value as a prop and then renders it out into a simple stylized div. We will have two pieces of functionality within the ui, a simple incrementing counter component, which we will maintain with the simple new state count and an increment utility function.

00:33

For the visual of the counter, we render a simple button via to the increment function and this button also displays the current count value. Now, for the second piece of functionality, we loop over a lyrics array and then render it out as the lyric components. Now these two pieces of functionality seem to coexist perfectly fine. We can increment the count as much as we want, and the lyrics display exactly how we would expect. Now there is a slight performance issue in our application and to demonstrate that, let's add a simple log statement within our lyric component to know whenever it gets rendered.

01:06

And of course, if you open up the developer tools, we see six log statements because we have six lyrics which get rendered when the application loads. However, a we thing that you might not expect is that whenever we increment the count, all of the lyric components still read render. Even though we know that there are no visual changes required for the individual lyrics. Now, even though you and I both know that the component doesn't need to read render in this particular case, react is not aware of this fact. Fortunately, there's a very easy way to declare the fact that our component only depends upon the props and is

01:40

therefore a pure component. So to declare that our component is pure, we simply need to wrap it in the React memo function. This function takes a component and then returns a component, but in the component that it returns, it makes sure that it doesn't get re rendered if the props do not change. And we can see this fact play out as we play around with the application. We only get the six initial render for the lyrics, but as we increment the value, there are no additional re renders. One thing to be aware of when using the React memo function is that it only does a shallow comparison by default.

02:15

Let's look at what that means for JavaScript. If we have two simple primitives within JavaScript, for example, two simple strings, the equality operator is actually quite reliable and will return true if the values of the strings are equal. However, this is not the case for objects within JavaScript. Just because the contents of the two objects are equal does not mean that they are triple equal to each other. Now, if you want these two objects to be equal, you would have to do some custom manual checks. For example, check that the value property of both of the two objects is equal. And in order to determine if a component should re gender

02:49

react, memo does a simple triple equal between the old and the new props. This means that a simple usage of React memo might not work if you have objects Or arrays as props, but fortunately there's an easy way to provide a custom equality function with React memo to demonstrate the issue as well as the solution. Consider a new lyric component that this time takes an object as a prop. In terms of rendering, it's pretty same as before. We log out whenever we are getting rendered and we wrap our function within React memo. Within our application, we load an array of objects

03:23

of lyrics and then map over this array passing in the individual lyric objects to the lyric component. Now, you would think that our use of react memo would prevent rear-ending of the lyric components in this particular case, however, it doesn't seem to be true. As we increment the count, all of the lyric components are getting rendered again and again. And the reason of course why this is happening is that the individual lyric objects are not triple equal to the ones that were passed in before. Now the fix is pretty easy. React memo actually takes a second function we can pass in as an argument and this function gets past the previous

03:59

and the next props. And we can choose to return true if we think that the component does not need to re-render because we think that the previous and the next props are effectively the same. Fortunately for us, our lyric object comes with a simple ID we can use for this particular check. And with this simple function in place as we implement the counter, you can see that our lyric components no longer re-render. I'll wrap things up there. As always, thank you for joining me and I will see you in the next one.