useMemo vs useCallback: When (Not) to Use Them
Performance hooks explained with practical rules of thumb and interview-ready examples.
Frontend Interview Team
February 08, 2026
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 countFix:
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.