skip to content

Both React.memo and useMemo are described as memoization in React, and candidates often use the names interchangeably. What does each one actually cache, and when would you reach for one rather than the other?

level: middleimportance: must knowfreq 72%

answer

  1. different units of caching
  2. one skips a render, one skips work
  3. wrapper versus in-render hook
  4. identity is a product too
  5. they pair, they do not substitute

basics

~20 s

React.memo caches a component's rendered output and skips re-running it when its props are unchanged. useMemo caches one value inside a component that still re-renders. One avoids a render; the other avoids a computation during a render.

solid answer

~40 s

They operate at different levels. `React.memo` wraps a component: when the parent re-renders, React compares props shallowly and, if they match, skips calling the component function at all — so its whole subtree is skipped too. `useMemo` is a hook called *inside* a component that is already rendering: it stores the result of a function and recomputes only when a dependency changes, but the component body around it still runs and still produces new elements. So `useMemo` never prevents a render; it makes a render cheaper. The two often work together — `useMemo` stabilises the identity of an object or array prop so that the `React.memo` wrapper on the child actually finds its props equal and bails out. Used alone, `useMemo` on a cheap calculation buys nothing.

code

javascript · 16 lines
javascript
import { memo, useMemo, useState } from 'react';

const PriceTable = memo(function PriceTable({ config, rows }) {
  return <div>{rows.length} rows in {config.currency}</div>;
});

export default function Page({ rows, locale, currency }) {
  const [query, setQuery] = useState('');
  const config = useMemo(() => ({ locale, currency }), [locale, currency]);
  return (
    <div>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      <PriceTable config={config} rows={rows} />
    </div>
  );
}

go deeper

for a junior

Learn the one-line split: React.memo wraps a whole component and can skip its render, while useMemo is called inside a component to reuse a computed value.

for a middle

Explain the mechanics of each — shallow props comparison versus dependency-array comparison — and state plainly that useMemo never prevents the surrounding component from re-rendering.

for a senior

Demonstrate the pairing in production terms: an unstable prop makes a memo wrapper worthless, so you stabilise identity where it crosses a boundary and confirm the bailout in the profiler rather than assuming it.

for a principal

Own the guidance that neither tool is a default. Set the expectation that memoization is added at measured boundaries with a stated reason, so the codebase does not accumulate wrappers nobody can justify.

## Two different units of caching The word "memoization" hides the important difference: **what is being cached**. `React.memo(Component)` caches a *rendered result*. It returns a wrapper component. When the parent re-renders and would normally re-render this child, React compares the new props with the old ones, shallowly, with `Object.is` per key. If they are all equal, React does not call the component function and reuses the element tree from last time. Everything below that component is skipped along with it. `useMemo(fn, deps)` caches a *value inside one render*. The component is already rendering — React is executing its body right now. When execution reaches the `useMemo` call, React compares the dependency array with the one from the previous render, again with `Object.is` per element. If nothing changed, it returns the stored value and does not call `fn`. If something changed, it calls `fn` and stores the new result. So the honest one-liner is: `React.memo` can stop a render from happening; `useMemo` only makes a render that is happening cost less. ## The two reasons to reach for useMemo **Skipping expensive work.** If the render computes something genuinely costly — sorting or grouping thousands of rows, building an index, parsing a large payload — and the inputs rarely change, `useMemo` skips that work on the renders where the inputs did not move. ```jsx const sorted = useMemo( () => rows.slice().sort((a, b) => a.name.localeCompare(b.name)), [rows] ); ``` **Preserving reference identity.** This is the reason people underrate. A value produced by `useMemo` keeps the same reference across renders while its dependencies hold still. That matters when the value is handed to something that compares by identity: a child wrapped in `React.memo`, or another hook's dependency array. Here the computation may be trivial and `useMemo` still earns its place, because the *identity* is the product, not the speed. ## How they compose The classic pairing: ```jsx const config = useMemo(() => ({ locale, currency }), [locale, currency]); return <PriceTable config={config} />; // PriceTable is wrapped in React.memo ``` Without the `useMemo`, `config` is a new object literal every render, `PriceTable`'s props comparison always fails, and the `React.memo` wrapper is pure overhead — a comparison that can never succeed. Adding `React.memo` to a child while leaving object props inline is one of the most common ways teams convince themselves they optimized something. The reverse is also true: `useMemo` on the parent with an unmemoized child buys nothing on the render path, because the child re-renders regardless of whether its props kept identity. ## When each one is the wrong tool Use `React.memo` when a component re-renders often with unchanged inputs *and* rendering it is not trivial. On a component that emits a few DOM nodes from primitive props, the props comparison costs roughly what the render costs, so you have added code and gained nothing measurable. Use `useMemo` when the computation is expensive or the identity is consumed by an equality check. On `useMemo(() => a + b, [a, b])` the machinery — allocating the closure, allocating the array, comparing the dependencies — is at least as expensive as the addition it avoids. ## The interview trap A frequent probe: "you wrapped the child in `React.memo` but it still re-renders — why?" The answer is almost never about `React.memo` being broken. Something in the props is a new reference each render, and the fix is to stabilise that value (often with `useMemo`, sometimes by hoisting a constant out of the component, sometimes by restructuring so the value never crosses the boundary). Being able to say which of the two tools addresses which half of the problem is the whole point of the question.

  • If you delete the useMemo around config in that example but keep React.memo on the child, what changes?
    The child re-renders on every keystroke. `config` becomes a new object literal each render, the shallow props comparison fails every time, and the `React.memo` wrapper turns into a comparison that can never succeed — a small net cost with zero benefit. The wrapper is only as good as the stability of the props feeding it.
  • Can useMemo ever be worth it for a computation that is genuinely cheap?
    Yes, when the point is identity rather than speed. If the returned object or array is passed to a memoized child or used in another hook's dependency array, keeping the same reference across renders is the product. Building a two-key object costs nothing; the stable reference is what stops downstream work.
  • Does useMemo guarantee the cached value survives until its dependencies change?
    Not as a semantic guarantee. React documents memoization as a performance optimization and may discard cached values, so `useMemo` must never be used where a recomputation would be incorrect — for instance to hold a value that must be created exactly once with an observable side effect. For that, use a ref or lazy state initialization.

React.memo is deciding not to cook the meal again because the order is identical. useMemo is cooking the meal but reusing yesterday's stock instead of boiling a new pot.

saying these in an interview costs you the question

  • Says useMemo stops the component from re-rendering
  • Treats React.memo and useMemo as interchangeable names
  • Wraps children in memo while passing inline object props
  • Thinks useMemo is only ever about speed, never identity
  • Claims React.memo caches values rather than rendered output

context