memo, useMemo and useCallback
AdvancedMemoisation trades memory and complexity for skipped work. Applied without measuring, it usually makes code slower to read and no faster to run.
Overview
These three tools exist to skip work that would otherwise repeat, and they are the most cargo-culted APIs in React. useCallback wrapped around every handler and useMemo around every value adds real cost — the comparison itself, the retained references, and the noise in the code — while fixing a problem that in most components does not exist. The correct order is: make it correct, measure, find the actual cost, then apply memoisation at that specific point. React Compiler is now doing much of this automatically, which makes the manual habit even less defensible.
What Each One Does
Three different things people conflate.
// memo — skip re-rendering a component when its props are shallow-equal
const ProblemRow = memo(function ProblemRow({ problem, onOpen }) { ... })
// useMemo — cache a computed VALUE between renders
const stats = useMemo(() => computeStats(attempts), [attempts])
// useCallback — cache a FUNCTION identity between renders
const onOpen = useCallback((slug) => navigate(`/p/${slug}`), [navigate])
// The three work together or not at all. memo compares props with
// Object.is, so a new function or object prop defeats it:
const Row = memo(RowImpl)
<Row problem={p} onOpen={() => open(p.id)} /> // new arrow each render
// -> memo never skips
// Both halves are required: memo on the child AND stable props.
// One without the other is pure cost.When It Is Justified
The specific situations, and how to confirm you are in one.
// 1. A genuinely expensive computation
const filtered = useMemo(
() => rows.filter(matches).sort(compare), // 10,000+ rows
[rows, query],
)
// Filtering 50 rows is microseconds. Measure before assuming.
// 2. A value used as a dependency, where a new identity would
// re-run an effect every render
const options = useMemo(() => ({ topic, limit }), [topic, limit])
useEffect(() => subscribe(options), [options])
// 3. Props to a memoised child that re-renders expensively —
// a chart, a large list, a code editor
// 4. Passing a value through context (memoise the provider value)
// How to confirm: React DevTools Profiler. Record the interaction,
// look for components with a long actual render time, and check
// "why did this render". If nothing is slow, do nothing.Cheaper Alternatives
Restructuring usually beats memoising, and the compiler may make the question moot.
// Move state down — the expensive sibling stops re-rendering
function Page() {
return <><SearchBox /><HugeList /></> // SearchBox owns the query
}
// Previously, query state in Page re-rendered HugeList on every keystroke.
// Pass children through — the child element is created by the
// parent's parent, so it is not recreated by this render
function Layout({ children }) {
const [open, setOpen] = useState(false)
return <div>{children}</div> // children do not re-render
}
// Compute during render instead of caching:
// derived values are usually cheap, and always correct.
// React Compiler (React 19) memoises automatically at build time,
// which removes most manual useMemo and useCallback. Where it is
// enabled, hand-written memoisation is mostly noise.
// And the cost of getting it wrong: a stale closure inside a
// useCallback with missing dependencies is a real bug, where no
// memoisation at all would merely have been slower.Key Points to Remember
- 1memo skips a re-render, useMemo caches a value, useCallback caches a function identity
- 2memo is defeated by a new object or arrow function prop — both halves are needed or neither helps
- 3Justified cases: genuinely expensive computation, dependency identity, memoised expensive children, and context values
- 4Confirm with the React DevTools Profiler rather than memoising on suspicion
- 5Moving state down or passing children usually beats memoising, and React Compiler removes most manual cases
Interview Questions
Sign in to ask AriaWhat is the difference between useMemo and useCallback?
Why does wrapping a child in memo often fail to prevent re-renders?
What is the cost of memoising everything by default?
Ask Aria about memo, useMemo and useCallback
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.