"In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again."
Memoization - Wikipedia
useMemo
is a hook used to memoize values inside a React component. It's a performance optimization to avoid recalculating expensive values on every render. You might be familiar with React's memo
function, which is similar, but is used to memoize React components themselves, to avoid said re-renders in the first place.
The TypeScript function signature of useMemo
:
type useMemo = <T>(factory: () => T, deps: Array<any>) => T;
The first argument is a factory function returning the value we want to memoize. Like useEffect
and useCallback
, the second argument to this hook, deps
, is a dependency array. Changes to the values passed to this array will trigger our factory function to rerun, returning a new value. If the values in the dependency array do not change, we'll instead receive the memoized value saved during the most recent execution of the factory function.
Example
function Todos({ todos, filterByStatus }) {
const filteredTodos = useMemo(() => {
return todos.filter(todo => todo.status === filterByStatus);
}, [todos, filterByStatus]);
return (
<ul>
{filteredTodos.map((todo, i) =>
<li key={i}>{todo.name}</li>
)}
</ul>
);
}
We receive the props todos
, an array of todo objects, and filterByStatus
, a string indicating the status we want to filter by, such as 'completed', 'in-progress', etc. If todo objects are added or removed from the todos
array, that will affect the resulting filteredTodos
. Likewise, the result will change if our filterByStatus
changes from 'in-progress' to 'completed'. We include both of these variables in the dependency array to signal to useMemo
that changes to these variables should trigger a recalculation of our computed filteredTodos
value.
Note that for useMemo
to detect that the todos
array is unchanged between renders, it must be equal by reference. The variables included in the dependency array will be compared to their previous values using strict equality (===
). Remember that arrays, objects, and functions are only equal by reference:
[1, 2, 3] === [1, 2, 3] // false
let x = [1, 2, 3];
x === x // true;
Using our example with the todos
array prop, if that prop comes from a parent component storing the value as state:
const [todos, setTodos] = useState([]);
...then todos
will remain referentially equal until setTodos
is called. You can test this out for yourself, by placing a console.log
statement inside your useMemo
's factory function, to see when it is triggered.
useMemo
has a lot of concepts in common with useEffect
and useCallback
, and yet, at least to me, useMemo
is a lot easier to understand. Perhaps that's because it seems less intuitive to memoize functions, in the case of useEffect
and useCallback
.
For a more detailed explanation of the dependency array, and other related concepts, you can check out:
My post on useEffect: React Hooks: How to Use useEffect