Back to React
React
medium
mid

How have you used React.memo, useCallback, useMemo, and other optimization techniques in your projects?

Yes — but selectively. `React.memo` for genuinely expensive children with stable prop references. `useCallback` for callbacks passed to memo'd children or as effect deps. `useMemo` for measurable expensive derivations. Other levers: virtualization, code splitting, `useDeferredValue`/`startTransition`, Suspense, Server Components. Profile first; the default of nothing is right most of the time.

4 min read·~8 min to think through

Interviewers want a real story, not a recital of the API. Pick a concrete case and walk through why you used (or removed) the optimization.

When each tool is the right answer

React.memo

tsx
const Row = React.memo(({ item }) => <div>{item.label}</div>);

Use when:

  • The component is genuinely expensive (heavy children, costly computation).
  • Its props are stable (parent uses useMemo/useCallback or stable refs).
  • The parent re-renders frequently.

Skip when:

  • The component is cheap; memo adds cost > savings.
  • Props change every render anyway (new object/array literals); memo bails immediately.

useCallback

tsx
const onClick = useCallback((id) => dispatch(select(id)), []);

Use when:

  • The callback is passed to a memo'd child.
  • The callback is an effect dep.

Skip for callbacks only consumed by non-memo'd children — no benefit.

useMemo

tsx
const filtered = useMemo(() => items.filter(p), [items, p]);

Use when:

  • The computation is measurably expensive.
  • The derived value is passed to memo'd children or effect deps.

Skip for trivial expressions — {count * 2} in JSX doesn't need useMemo.

useDeferredValue / startTransition

For non-urgent renders triggered by user input — see [[how-would-you-improve-interaction-to-next-paint-inp]].

Virtualization

For long lists. See [[how-would-you-optimize-react-rendering-performance-in-large-lists-eg-10k-rows]].

Code splitting + lazy

Per-route + heavy modals. See [[why-is-code-splitting-better-than-loading-everything-at-once]].

Server Components / streaming SSR

Reduce client JS for non-interactive parts.

A real story (template)

"On the dashboard, the legend component for our chart was re-rendering every time we hovered a data point — 60 renders per second. Profiler showed the legend's render was 4ms each; not slow individually but summed to dropped frames. The data passed in was a new object literal from the parent. Two fixes: useMemo on the data prop in the parent, React.memo on the legend. INP improved 250ms → 90ms. The lesson: stable prop references are required for memo to do anything."

When I've removed memoization

"Inherited a codebase with useCallback on every handler. Profiled — most handlers were passed to non-memo'd children where the wrapping cost > benefit. Removed half the memoization; commit time dropped slightly and the code got more readable."

Anti-patterns

  • Memo + new object literal prop (memo always bails).
  • useMemo for cheap expressions.
  • useCallback on handlers consumed by non-memo'd children.
  • React.memo on every component without measuring.

Interview framing

"Yes — but selectively. React.memo for expensive components with stable prop refs, useCallback for callbacks passed to memo'd children or used as effect deps, useMemo for measurably expensive derivations. Profile first — memo costs allocation + comparison; if savings don't beat that, you've made things worse. Real wins usually come from structural fixes: virtualization, code splitting, useDeferredValue, RSC. I've removed unnecessary memoization more often than I've added it. Story: stable prop refs are required for memo to actually do anything — a new object literal in the parent defeats it."

Follow-up questions

  • Have you removed useMemo / useCallback in a codebase? Why?
  • When does memo defeat itself?
  • What's a structural perf fix you've made?

Common mistakes

  • Memo + inline object props.
  • useCallback everywhere reflexively.
  • useMemo for cheap expressions.

Performance considerations

  • Memo wins only on measured hot paths with stable refs. Structural fixes (virtualization, RSC) outperform micro-memo.

Edge cases

  • Memo'd component with children prop — children change references unless lifted.
  • Custom equality on memo for object props.
  • useEvent / useEffectEvent (React 19 RFC) as alternative to useCallback for stable callbacks.

Real-world examples

  • TanStack Virtual rows, charting libraries, design system primitives.

Senior engineer discussion

Seniors story-tell with measurement, articulate when memo bailed because of unstable refs, and prefer structural fixes when possible.

Related questions