useMemo
Caches the result of a calculation between re-renders until one of its dependencies changes.
const cachedValue = useMemo(calculateValue, dependencies)- Added in React 16.8.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$calculateValue | () => T | The function calculating the value you want to cache. It should be pure, take no arguments, and return a value of any type. React calls it during the initial render, and on later renders only when a dependency has changed. |
$dependencies | Array<any> | The list of all reactive values referenced inside calculateValue — props, state, and any variables and functions declared directly in the component body. The list must have a constant number of items and be written inline like [dep1, dep2, dep3]. React compares each dependency with its previous value using Object.is. |
Return value
anyOn the initial render, useMemo returns the result of calling calculateValue with no arguments. During next renders, it returns the already stored value from the last render if the dependencies haven't changed, or calls calculateValue again and returns its result.
Errors
Throws an invalid-hook-call error if used outside a component or custom Hook body, or inconsistently across renders because it was placed inside a condition or loop.
Changelog
- PHP 18.0.0In Strict Mode, React calls the calculation function twice in development to surface accidental impurities. One result is discarded; production is unaffected.
- PHP 18.0.0React throws away the cached value if the component suspends during the initial mount, so the calculation runs again after the data resolves.
useMemo caches the result of a calculation across renders. React runs the calculation on the first render, then hands back the stored result until one of the dependencies changes under an Object.is comparison.
When to reach for it
- The derivation is genuinely slow. Filtering or sorting thousands of rows, building a lookup index, parsing. Measure first — if the work takes well under a millisecond, the memo bookkeeping costs more than it saves.
- You need a stable identity, not speed. An object or array passed to a component wrapped in
memo, or used in the dependency array ofuseEffector another Hook. Without the memo, a fresh reference every render defeats the child's bail-out or re-fires the Effect.
What trips people up
The most common failure is a dependency that is itself recreated on every render — an inline object, an array literal, or a function declared in the component body. Because its reference is new each time, the cache never hits and the calculation runs every render anyway. Memoize that dependency too, or hoist it out of the component.
The second is syntax: useMemo(() => ({ a }), [a]) returns an object, while useMemo(() => { a }, [a]) returns undefined, because the braces are read as a function body.
Treat the result as a cache, not a guarantee — React may discard it and recompute.
Example
When to use it
- Keep a sorted or filtered view of a large table from being rebuilt on every keystroke elsewhere in the form.
- Give a memo-wrapped chart component a props object whose identity only changes when the chart data actually changes.
- Build a Map index from an array once, so lookups inside a render loop stay O(1) without rebuilding the Map each render.
- Stabilize a context value object so consumers do not re-render whenever the provider re-renders.
Frequently asked questions
Should I wrap every calculation in useMemo?
No — useMemo has its own cost, and for cheap work it is a net loss. Reach for it when a derivation is measurably slow, or when you specifically need a stable object or array identity for a memo-wrapped child or a dependency array.
Why does my useMemo recalculate on every render?
Because at least one dependency has a new identity each render. Objects, arrays, and functions created in the component body are new references every time; memoize or hoist them, or depend on the primitive fields inside them instead.
What is the difference between useMemo and useCallback?
useMemo caches the value your function returns, while useCallback caches the function itself. useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).
More examples
Stabilizing an object used as an Effect dependency
Clicking the button re-renders Report repeatedly, but "Effect runs" stays at 1 because `options` keeps the same identity. Deleting the useMemo wrapper and passing a plain `{ currency: "EUR", precision: 2 }` literal makes the Effect fire on every render, which in this case sets state and loops.
import { useEffect, useMemo, useState } from "react";
function useFormatter(options) {
const [runs, setRuns] = useState(0);
useEffect(() => {
// Re-runs whenever the identity of `options` changes.
setRuns((n) => n + 1);
}, [options]);
return { runs, label: `${options.currency}/${options.precision}` };
}
export default function Report() {
const [tick, setTick] = useState(0);
// Without useMemo this object is new on every render, so the Effect
// above would re-run forever.
const options = useMemo(() => ({ currency: "EUR", precision: 2 }), []);
const { runs, label } = useFormatter(options);
return (
<div>
<button onClick={() => setTick(tick + 1)}>Re-render ({tick})</button>
<p>Formatter: {label}</p>
<p>Effect runs: {runs}</p>
</div>
);
}Feeding a memo-wrapped child a stable array
At zoom 1 the chart shows "Plotting 8 points, peak 8". Typing in the label input re-renders Dashboard, but `points` keeps its identity so the memo-wrapped Chart skips re-rendering entirely. Pressing zoom changes the dependency, a new array is produced, and Chart re-renders with peak 16.
import { memo, useMemo, useState } from "react";
const Chart = memo(function Chart({ points }) {
return (
<p>
Plotting {points.length} points, peak {Math.max(...points)}
</p>
);
});
export default function Dashboard() {
const [zoom, setZoom] = useState(1);
const [label, setLabel] = useState("Q1");
const points = useMemo(
() => Array.from({ length: 8 }, (_, i) => (i + 1) * zoom),
[zoom]
);
return (
<div>
<input value={label} onChange={(e) => setLabel(e.target.value)} />
<button onClick={() => setZoom(zoom + 1)}>zoom: {zoom}</button>
<Chart points={points} />
</div>
);
}
Discussion