Menu

React useMemo Hook: Cache Expensive Calculations

useMemo caches the result of a calculation between renders and recomputes it only when its dependencies change. Learn the syntax, how to keep an object stable for a memo child, and when useMemo is not worth it.

This page includes runnable editors - edit, run, and see output instantly.

useMemo caches the result of a calculation between renders. React runs your function on the first render, keeps the result, and hands back that same result on later renders until one of the values in the dependency array changes.

Open the Console and toggle the theme a few times: nothing is logged, and the toggle feels instant. Switch tabs and filtering for active appears, with a short pause, because tab is a dependency. Now replace the useMemo line with const visible = filterTodos(todos, tab); and toggle the theme again: every click logs and stalls for 100 ms.

The syntax

const cachedValue = useMemo(calculateValue, dependencies);

const visible = useMemo(() => filterTodos(todos, tab), [todos, tab]);
  • calculateValue is a function with no arguments that returns the value you want to cache. React calls it during render.
  • dependencies is the list of every value from the component that the function reads: props, state, and variables declared in the component body.
  • On every render React compares each dependency with its value from the previous render using Object.is. If all match, it returns the cached value without calling your function. If any differs, it calls the function and caches the new result.

useMemo is a hook, so it follows the rules of hooks: call it at the top level of the component, not inside a loop or a condition.

Dependencies must be complete

Leave a value out of the array and the cache keeps serving a result computed from its old value. Here the total reads qty but only lists price:

Click Quantity: it goes to 2, but the total stays at 10 and nothing is logged. Click Price and the total jumps to 22, because only then does the calculation run again, now reading the current qty. Add qty to the array and both buttons update the total. The react-hooks/exhaustive-deps lint rule catches this mistake in a real project.

The opposite mistake is a dependency that is new on every render, such as an object created in the component body. It never matches its previous value, so the cache is never used:

const filters = { tab, query }; // a new object every render
const visible = useMemo(() => applyFilters(todos, filters), [todos, filters]); // recomputes every time

// List the primitive values instead
const visible = useMemo(() => applyFilters(todos, { tab, query }), [todos, tab, query]);

Keeping an object stable for a memo child

The second reason to reach for useMemo has nothing to do with slow calculations. A child wrapped in memo skips its render only when every prop is the same as last time. An object literal written in the component body is a new object on every render, so it defeats memo even when its contents did not change. useMemo keeps the same object until its inputs change.

Type in the note field and Chart stays quiet, while Switch unit logs one render. Replace the useMemo call with a plain const options = { unit, max: unit === 'km' ? 100 : 62 }; and every keystroke in the note logs render Chart.

The same applies to an effect that depends on an object. If an effect lists options in its dependencies and options is rebuilt on every render, the effect runs after every render. useMemo fixes that, but the simpler fix is often to create the object inside the effect and depend on the primitive values:

useEffect(() => {
    const options = { serverUrl, roomId };
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
}, [serverUrl, roomId]);

When not to use useMemo

Most calculations in a component are cheap: adding numbers, joining two strings, filtering a few dozen items. Caching those saves next to nothing: React still stores the value and compares the dependencies on every render, and the code gets harder to read.

// No useMemo needed
const fullName = first + ' ' + last;
const doneCount = todos.filter((t) => t.done).length;

To find out whether something is slow, time it. Wrap the calculation in console.time('filter') and console.timeEnd('filter'), run the interaction, and look at the number. As a rough guide, work that takes a millisecond or more on your machine is a candidate, keeping in mind that your users' devices may be slower. Before reaching for useMemo, also check whether the work belongs in render at all: data that never changes can be computed once outside the component, and state that only one small part of the page uses can move into a smaller component (see rendering).

useMemo vs useEffect for derived values

A common pattern is to compute a value in an effect and store it in state:

// Two renders per change, and the first one has stale data
const [visible, setVisible] = useState([]);
useEffect(() => {
    setVisible(filterTodos(todos, tab));
}, [todos, tab]);

// One render, always in sync
const visible = useMemo(() => filterTodos(todos, tab), [todos, tab]);

The effect version renders once with the old visible, then runs the effect, sets state and renders again. That is an extra render, and in the first one the list does not match the tab. Computing during render with useMemo (or with no hook at all, if the work is cheap) gives the right value in the same render. Effects are for synchronizing with things outside React, not for deriving one piece of state from another.

A performance hint, not a guarantee

useMemo tells React it may reuse a result. It does not promise that it will. React keeps the cached value in normal operation, but it can throw it away, for example when you edit the file during development, or when a component suspends during its first mount. Write the code so that it is still correct if the calculation runs again: the function must be pure, with no side effects such as requests, subscriptions or changing variables outside it. Side effects belong in an event handler or an effect.

In development, under StrictMode, React calls your calculation function twice to help you spot impure code. The preview here runs like a production build, so you will see each filtering for log once.

useMemo and React Compiler

React Compiler 1.0 is a build tool that analyses your components and adds memoization automatically, caching calculations and JSX in the places where useMemo, useCallback and memo would help. In a project that uses it, you will write fewer of these by hand. Manual useMemo still works alongside the compiler, it is still the tool when the compiler is not set up, and you will meet it in most existing React code.

Frequently Asked Questions

What does useMemo do in React?

It calls your calculation function on the first render, stores the result, and on later renders returns the stored result as long as every value in the dependency array is the same as last time (compared with Object.is).

When should I use useMemo?

When a calculation is noticeably slow and its inputs rarely change, or when you need an object or array to keep the same reference between renders because a memo child or an effect depends on it. For cheap work, skip it.

What is the difference between useMemo and useCallback?

useMemo caches the value your function returns. useCallback caches the function itself. useCallback(fn, deps) is the same as useMemo(() => fn, deps).

Does useMemo guarantee the value is never recalculated?

No. It is a performance hint. React may throw the cached value away in some cases, for example in development when you edit the file, so your code must still work correctly if the calculation runs again.

Do I still need useMemo with React Compiler?

React Compiler can add memoization automatically when your build uses it. Without the compiler, or for cases it skips, manual useMemo still works the same way and is still common in existing code.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED