Back to React
React
medium
mid

How does React.memo differ from useMemo and useCallback?

React.memo: HOC that prevents a component from re-rendering when its props are shallow-equal to the previous render. useMemo: caches a COMPUTED VALUE across renders, recomputes when deps change. useCallback: caches a FUNCTION reference across renders, returns same function when deps unchanged. React.memo is about whether a child renders; useMemo/useCallback are about whether passed values change reference. Use together: memoized child + stable callback/value props.

7 min read·~5 min to think through

Three different tools that often appear together but solve different problems.

React.memo

A higher-order component that wraps a function component and skips its re-render if props haven't (shallow-)changed:

jsx
const Greeting = React.memo(function Greeting({ name }) {
  return <h1>Hi {name}</h1>;
});

When the parent re-renders, React shallow-compares the new { name } against the previous. If equal, Greeting doesn't run; React reuses the previous output.

Use when: child renders are expensive and props are usually stable.

Won't help if: parent passes new object/array/function references each render — shallow compare returns "different" every time.

useMemo

Caches a computed value across renders:

jsx
const sorted = useMemo(() => items.sort(compare), [items]);

If items is the same reference as last render, sorted is returned from cache; the sort doesn't run.

Use when:

  • Computation is expensive (heavy filter/sort, parse).
  • Or the result is passed as a prop to a memoized child and you need a stable reference.

Don't use for:

  • Trivial computations (useMemo(() => x + 1, [x]) is overhead, not optimization).
  • Wrapping primitives (a string/number compare is already cheap).

useCallback

Caches a function reference:

jsx
const onClick = useCallback(() => doStuff(id), [id]);

Same id → same function reference across renders. Different id → new function.

useCallback(fn, deps) is equivalent to useMemo(() => fn, deps) — just syntactic sugar for the common case of caching functions.

Use when: passing a function to a memoized child or to a dep array of an effect.

Don't use for: passing to a non-memoized child (no benefit).

How they compose

The canonical pattern:

jsx
const MemoChild = React.memo(Child);

function Parent({ items, id }) {
  const filtered = useMemo(() => items.filter(i => i.active), [items]);
  const onSelect = useCallback((x) => select(x, id), [id]);
  return <MemoChild items={filtered} onSelect={onSelect} />;
}
  • React.memo short-circuits the child's re-render…
  • …but only works if props are stable…
  • …so useMemo stabilizes the filtered array reference…
  • …and useCallback stabilizes the function reference.

Remove any one piece and the memoization breaks (or becomes useless overhead).

When to use which

GoalTool
Skip a child's re-render when its props haven't changedReact.memo
Avoid recomputing an expensive valueuseMemo
Cache a value so it's reference-stable for a memo child or effect depuseMemo
Cache a function so it's reference-stableuseCallback

What they DON'T do

  • None of them make the parent skip its re-render — only the child or the value/function changes.
  • None of them deep-compare — shallow only.
  • None of them detect mutationitems.push(x) doesn't trigger anything; React sees the same reference.

Common mistakes

  • React.memo without stable props: parent passes { data } literally → new object → memo never fires. Memoize the data or hoist.
  • useMemo around primitives: useMemo(() => 5, []) is wasteful.
  • useCallback for a function only used once: overhead with no payoff.
  • Custom comparator that compares everything deeply: defeats the perf goal.
  • Memoizing everything by default: the overhead can exceed the savings.

Custom equality for React.memo

jsx
React.memo(Child, (prev, next) => prev.id === next.id);

Compare only what matters. But: usually a sign that the props are too coarse — refactor to pass narrower props.

When to skip them all

If the child renders cheaply, don't memoize. React's reconciler is fast; cheap re-renders are fine. Memoization is targeted, not blanket.

React 19 compiler

The upcoming compiler auto-memoizes values and callbacks. Most useMemo/useCallback boilerplate goes away; React.memo too in many cases. The underlying concepts remain useful for understanding what the compiler does.

Mental model

  • React.memo decides whether the function component runs.
  • useMemo caches a value.
  • useCallback caches a function (which is just a value).

They compose to keep memoized children from re-rendering when nothing relevant changed.

Follow-up questions

  • When is useMemo wasteful?
  • Why doesn't React.memo deep-compare by default?
  • How does the React 19 Compiler change usage?
  • Can you use React.memo on class components?

Common mistakes

  • React.memo with always-fresh prop references — memo never short-circuits.
  • useMemo around primitives or trivial computations — net negative.
  • useCallback for functions passed to non-memoized children.
  • Custom comparator that deep-compares everything — kills the perf goal.
  • Memoizing every value by default.
  • Forgetting React.memo only short-circuits the wrapped component's render, not children.

Performance considerations

  • Memoization is targeted, not universal. The win shows up only when the wrapped child is expensive AND the props are stable. Blanket memoization adds CPU + memory overhead with no payoff. The React 19 Compiler will auto-memoize, removing most manual usage.

Edge cases

  • React.memo doesn't memoize children — wrap deeper if needed.
  • useMemo can be discarded under memory pressure (in some implementations) — don't rely on it for correctness.
  • useCallback is just useMemo(() => fn, deps).
  • Refs (useRef) don't trigger re-renders and don't need memoization.
  • Class components: use shouldComponentUpdate or PureComponent instead.

Real-world examples

  • Form libraries (React Hook Form) memoize aggressively to avoid re-rendering on every keystroke.
  • Big tables (TanStack Table) memoize row components.
  • React docs explicitly warn against premature memoization.

Senior engineer discussion

Seniors profile before memoizing, apply the trio together when needed, and avoid blanket usage. They distinguish the three tools' purposes precisely and know the React 19 Compiler will absorb most of the boilerplate.

Related questions