useMemo vs useCallback: When (Not) to Use Them

Performance hooks explained with practical rules of thumb and interview-ready examples.

F

Frontend Interview Team

February 08, 2026

2 min read
useMemo vs useCallback: When (Not) to Use Them

30‑second interview answer

useMemo memoizes a computed value and useCallback memoizes a function reference. They help performance by keeping references stable so React can skip work (e.g., via React.memo) or avoid re-running expensive computations. They are not free—they add complexity and can make code slower if used everywhere. Use them when you have measured performance issues or you must keep references stable.


The problem these hooks solve

In React, every render creates new objects and functions:

function Parent() {
  const onClick = () => console.log('hi');
  return <Child onClick={onClick} />;
}

If Child is memoized (React.memo), it will still re-render because onClick is a new reference each time.


useCallback: memoize a function

const onClick = useCallback(() => {
  doSomething(id);
}, [id]);

Now onClick keeps the same reference until id changes.

Common interview trap: “Is useCallback always faster?”

No.

  • It prevents new function creation, but…
  • It adds memory and dependency tracking overhead.

Use it when:

  • you pass a callback to a memoized child
  • you pass a callback to a hook that depends on stable refs (e.g., event subscription)

useMemo: memoize a value

const filtered = useMemo(() => {
  return items.filter((x) => x.active);
}, [items]);

Use it when:

  • computation is expensive
  • and dependencies change less frequently than renders

The most useful pattern: stabilize props for memoized children

const Child = React.memo(function Child({ data, onPick }) {
  // ...
});
 
function Parent({ items }) {
  const data = useMemo(() => items.map(/* ... */), [items]);
  const onPick = useCallback((id) => {/* ... */}, []);
 
  return <Child data={data} onPick={onPick} />;
}

The biggest pitfall: wrong dependencies

If you forget deps, you get stale values.

Bad:

const onClick = useCallback(() => {
  console.log(count);
}, []); // ❌ stale count

Fix:

const onClick = useCallback(() => {
  console.log(count);
}, [count]);

Another pitfall: memoizing everything

Overusing memoization can:

  • clutter code
  • increase memory usage
  • make renders slower (more work to compare deps)

Interview-ready line:

“I use memoization sparingly and confirm with profiling.”


Alternatives that often work better

1) Split components

Smaller components can reduce re-render cost.

2) Move state down

If state is local, keep it local so it doesn’t re-render large trees.

3) Use React.memo only where beneficial

Memoizing the right child components is often enough.


Practical examples interviewers like

Prevent re-render of expensive child

const ExpensiveChart = React.memo(({ data }) => {
  return <Chart data={data} />;
});
 
function Dashboard({ points }) {
  const data = useMemo(() => transform(points), [points]);
  return <ExpensiveChart data={data} />;
}

Stable handler for subscription

const handler = useCallback((e) => {
  // ...
}, []);
 
useEffect(() => {
  window.addEventListener('resize', handler);
  return () => window.removeEventListener('resize', handler);
}, [handler]);

Mini Q&A

Q1: Difference between useMemo and useCallback?

  • Value vs function reference.

Q2: When should you use them?

  • When you need stable references or expensive computations; confirm with profiling.

Q3: Why are they not free?

  • Dependency tracking + memory overhead + code complexity.

Summary checklist

  • I can define both hooks in one line.
  • I know when they help (memoized children, expensive compute).
  • I can explain the downsides.
  • I understand dependency correctness.