React useMemo Demystified

Pro Members Only

This lesson is available if you have an active subscription.

Alternatively, some member might be able to gift it to you.

If you already have a subscription, you can sign in.

React useMemo Demystified

Subscription Required

You must have an active subscription to access this content.
If you already have a subscription, you can sign in.

React useMemo hook use cases

The React.useMemo hook is fundamentally a memoization utility. In computing, memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls to pure functions and returning the cached result when the same inputs occur again.

That said it has multiple use cases but for React we can narrow them to two fundamentally categories:

  • Caching results of expensive function calls
  • Preserving referential integrity of values to prevent needless re-renders

useMemo for expensive function calls

A fundamental use case for useMemo that aligns with the fundamental concept of memoization is caching the results of expensive function call so they don't happen in the standard render path of a component.

For example, in the below example the fibonacci call is only made when the num in the dependency array changes. So it is not called when other things change (like the name field).

App.tsx
import { useState } from "react";
import { useMemo } from "react";

function fibonacci(index: number): number {
if (index <= 1) return 1;
return fibonacci(index - 1) + fibonacci(index - 2);
}

export default function App() {
const [name, setName] = useState("");
const [num, setNum] = useState(0);

const result = useMemo(() => fibonacci(num), [num]);

return (
<>
<input
type="number"
value={num}
onChange={(e) => setNum(Number(e.target.value))}
/>
<div>Result: {result}</div>

<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<div>Name: {name}</div>
</>
);
}

Referential equality recap

Here is a quick recap of referential integrity, a concept we covered in our JavaScript course:

const alpha = ['apple'];
const beta = ['apple'];
alpha === beta; // false

alpha === alpha; // true
beta === beta; // true

{
const fruits = ['apple', 'banana'];
const n = 1;
const items1 = original.slice(0, n); // ['apple']
const items2 = original.slice(0, n); // ['apple']
items1 === items2; // false
}

{
const fruits = ['apple', 'banana'];
const n = 1;
// render 1
const items1 = useMemo(() => original.slice(0, n), [n]); // ['apple']
// render 2
const items2 = useMemo(() => original.slice(0, n), [n]); // ['apple']
// Thank you useMemo!
items1 === items2; // true
}

useMemo to preserve referential equality

By utilizing useMemo we can cache objects that are not changing to ensure that pure components can reliably use equality to prevent re-renders.

This is demonstrated in the below example where the call to slice is memoized to preserve its referential integrity.

App.tsx
import { useState, memo, useMemo } from 'react';

/** Example pure component */
const List = memo((props: { items: string[] }) => {
console.log('List rendered');
return (
<ul>
{props.items.map((item, i) => <li key={i}>{item}</li>)}
</ul>
);
});

const fruits = ['apple', 'banana', 'orange', 'pear', 'grape'];

export default function App() {
const [name, setName] = useState('');

const [count, setCount] = useState(0);
const inc = () => setCount(count => Math.min(count + 1, fruits.length));
const dec = () => setCount(count => Math.max(count - 1, 0));

/** Preserve referential integrity with `useMemo` */
const items = useMemo(() => fruits.slice(0, count), [count]);

return (
<>
<input value={name} onChange={e => setName(e.target.value)} />

<div>
<button onClick={dec}>-</button>
<span> {count} </span>
<button onClick={inc}>+</button>
</div>

{/* Use the result of `useMemo` for the pure component */}
<List items={items} />
</>
);
}
javascript
typescript
react
playwright

Enjoy free content straight from your inbox 💌

No spam, unsubscribe at any time.

Transcript

00:00

There are lots of features provided by React to help you optimize the performance of your application. And one of those features, which is pretty easy to use, is the use memo hook. So let's take a look. One big use case for the use memo hook is that it saves us time by preventing expensive recalculations. A function that quickly becomes expensive is the famous Fibonacci function. This function is designed to return the Nth Fibonacci number. We have two distinctive features operating within our ui. The first is a simple number which will be used to calculate the Fibonacci result for that index.

00:33

And the second will be a simple string value which the user can change to demonstrate the performance of the application. Within our ui, we will have a simple input of type number wired to the number variable and then a D displaying the resulting fazi value. And for our second piece of functionality, which is that simple name string, we will have a simple input wired to that straight variable in a simple day, displaying the current value of the name. Now while the Fibonacci function is quite fast, for example, of course the value for zero is quite quick to calculate.

01:06

The name field works perfectly fine, it's quite responsive and everything looks to be okay, but if you add a bigger input to the Fibonacci function, it becomes quite slow and you would expect it to be slow. But the UI is also slow if you modify the name field. And the reason why is because every single time you modify the name, the function has to render again, which means that the number gets passed to Fibonacci and that takes a while and makes an entire application a bit slower than we would like it to be. This is exactly the kind of problem that the use memo hook is designed to solve. Memo stands for memorization,

01:39

which is a computer science term, meaning storing a computation result in a case and then retrieving that same information from the case the next time it is needed instead of computing it again and again. The use memo hook is provided by React. So we bring it in the same way we brought in the use state hook. This hook can be used to wrap any function that we want to memorize. For example, we want to memorize the result of the Fibonacci call. So we wrap that with a use memo, give it a callback that will invoke Fibonacci. And then we want this callback to execute every single time the number variable is changed.

02:13

So therefore we provide that as a dependency array as a second argument to use memo. So now the call to Fibonacci will only be made if the number of variable changes and then the result will be memorized for future renders. So now as we play around with the ui, the Fibonacci will be recalculated if we modify the number and if we provide a sufficiently bigger number, for example, 39, the result might take a while to calculate. However, it's not going to be recalculated if the rendering is happening because of modifications other than modifying the number. So the name input continues to be responsive

02:46

because Fibonacci is not going to get recalculated Caching results of expensive competitions is the main use case for use memo. But even if the competition isn't expensive, another use case for use memo is to ensure a referential equality Of a calculated result to prevent pre renders. To demonstrate that, consider a simple list component, which is going to be a pure component because we are going to wrap it up with React memo. You've seen memo before when we looked at pure components. The list component takes an items array and then renders out a simple unordered list with the list items pointing

03:19

to the individual items of the array. The objective of using the memo function over here is that if the provided items array is the same, then we don't expect the list component to re-render. And just to make sure of that, we log out list rendered every single time it does get rendered. Now we plan to display some fruits within this list. So we create a simple array containing some popular fruits, and then within our main application component, we plan to have two distinct pieces of functionality. One, again, just a simple name which we intend to eventually wire to a simple input

03:52

and then account, which will determine the number of items that we show from the fruits array. We have some utility functions to increment and decrement the count value. And then based on the current count, we will pick that many items from the fruits array to store it into a new items array. Now in terms of ui, we have that simple input which allows the user to display and edit the current name value. And then we have a simple D containing the decrement, the increment buttons, and also display the current count. And finally, based on the count, the calculated result of the items is what we passed to our pure list component.

04:28

Now if you run the application, you can see that the list gets S rendered originally because of course it has to render once. And then as you modify the count, we will calculate a different result for items and we do expect the list to get re rendered. However, as we modify the name, even though the list items that are being rendered are the same, visually the list component is being rendered again and again despite it being wrapped with that memo function to make it a pure component. So the question that I want you to think about is why is the list component getting re rendered if we are wrapping it with the memo function

05:02

and the items contain the same strings between the different renders? The answer to this mystery lies in JavaScript. Just because two arrays contain the same members does not mean that they are equal. They are still different instances of the arrays and only the instance of the array is equal to itself. So alpha is equal to alpha and beta is equal to beta. And this is sort of what is happening within our code. We have an input fruits array and then we try to get, for example, the first item from that array as a new array. Every single time we call Slice, we get a new instance.

05:36

And therefore items one is not equal to items two, even though they have the same members and therefore the list component is re rendering. The solution to this problem is that we should store the result of the call to slice, and as long as N is the same, we should not calculate Slice again and just return the same instance. And that is exactly what is provided by the use memo hook. And if you were to wrap the calls to Slice with used memo and passing in as a dependency array, then we will get back the same instance without the slice getting called again. And therefore items one will be equal to items two

06:09

and the list component should not re-render. So let's jump back to our application, do the required changes, wrap the food star slice with use memo, and only reinvoke that if the count value changes how with this simple change in place, if we jump back to our application, of course the items will get recalculated and the list will re-render if we change the count. However, if you cause the app component to re-render by only modifying the name, then the items will exactly be the same as what they were before and therefore the list component will not re-render. As we can see by the absence

06:41

of further consult log statements. Now, one final thing that I want to mention is that user memo does consume some memory because of course it has to store the previous value. So don't use it if you don't need to. I'll wrap things up there. Thank you for joining me and I'll see you in the next one.