useMemo vs useCallback: What's the Difference?
Short answer
useMemo caches a computed value; useCallback caches a function definition. They are the same hook underneath — useCallback(fn, deps) is exactly useMemo(() => fn, deps). Both exist for referential stability, not raw speed: they keep a prop or dependency from changing identity on every render.
| useMemo | useCallback | |
|---|---|---|
| Caches | The result of calling a function | The function itself |
| Returns | Whatever your function returned | A function you can call later |
| Signature | useMemo(() => value, deps) | useCallback(fn, deps) |
| Equivalent to | — | useMemo(() => fn, deps) |
| Main job | Referential stability + skipping expensive work | Referential stability |
| Typical use | Derived data passed to a memoized child | Event handler passed to a memoized child |
| Useless without | A memoized consumer or a genuinely costly calc | React.memo, or an effect dependency |
The one-line difference
useMemo runs your function and caches what it returned. useCallback does not run your function at all — it caches the function itself so you get the same reference back on the next render. That is the entire distinction, and everything else follows from it.
const total = useMemo(() => items.reduce((s, i) => s + i.price, 0), [items]);
// total → 1250 (a number — the function ran)
const onSelect = useCallback((id) => setSelected(id), []);
// onSelect → ƒ (id) {} (a function — it has NOT run)They are so closely related that useCallback is not really a separate feature. It is a shorthand, and you can prove it in one line:
1useCallback(fn, deps)
2// is exactly equivalent to
3useMemo(() => fn, deps)
4
5// So this…
6const handleClick = useCallback(() => save(id), [id]);
7// …and this are the same thing:
8const handleClick = useMemo(() => () => save(id), [id]);
9// → note the double arrow: useMemo must RETURN the functionWhy they exist: referential stability, not speed
The most common misconception is that these hooks make your app faster by avoiding work. That is a secondary benefit at best. Their real purpose is to stop a value's identity from changing on every render.
In JavaScript, every object, array and function you create is a brand-new reference. Two structurally identical values are not equal:
1{} === {} // → false
2[1, 2] === [1, 2] // → false
3(() => {}) === (() => {}) // → false
4
5// So on every render of this component, onClick is a NEW function:
6function Parent() {
7 const onClick = () => console.log("hi"); // new reference, every render
8 return <Child onClick={onClick} />; // Child sees a "changed" prop
9}That is the problem both hooks solve. React.memo compares props shallowly with Object.is; a new function reference fails that check every time, so the memoized child re-renders anyway and your optimisation does nothing. useCallback hands back the same reference, so the comparison finally passes.
When useCallback actually helps
There are exactly two situations. Outside them, useCallback costs you and gives nothing back.
- You pass the function to a component wrapped in React.memo.
- You use the function as a dependency of useEffect, useMemo, or another useCallback.
1const Row = React.memo(function Row({ item, onSelect }) {
2 console.log("render", item.id);
3 return <li onClick={() => onSelect(item.id)}>{item.name}</li>;
4});
5
6function List({ items }) {
7 const [query, setQuery] = useState("");
8
9 // WITHOUT useCallback: typing in the input re-creates onSelect,
10 // so every Row re-renders on every keystroke. React.memo is defeated.
11 const onSelect = useCallback((id) => console.log("picked", id), []);
12
13 return (
14 <>
15 <input value={query} onChange={(e) => setQuery(e.target.value)} />
16 <ul>{items.map((i) => <Row key={i.id} item={i} onSelect={onSelect} />)}</ul>
17 </>
18 );
19}
20// → with useCallback: no "render" logs while typing
21// → without it: one "render" log per row, per keystroke1// Without useCallback, fetchData is new every render,
2// so the effect re-runs every render → infinite request loop.
3const fetchData = useCallback(async () => {
4 const res = await fetch(`/api/items?q=${query}`);
5 setItems(await res.json());
6}, [query]);
7
8useEffect(() => { fetchData(); }, [fetchData]); // → runs only when query changesWhen useMemo actually helps
useMemo has the same two cases as useCallback — memoized consumer, dependency array — plus a third that is genuinely about performance: a computation expensive enough to be worth caching.
1// 10,000 rows, sorted and filtered on every keystroke without useMemo.
2const visible = useMemo(() => {
3 return rows
4 .filter((r) => r.name.toLowerCase().includes(query.toLowerCase()))
5 .sort((a, b) => a.name.localeCompare(b.name));
6}, [rows, query]);
7// → recomputes only when rows or query change, not when unrelated state does"Expensive" is doing real work here. Sorting ten items is not expensive; sorting ten thousand with a locale-aware comparator is. If you cannot measure the difference in the React Profiler, the memo is not paying for itself.
The other high-value use of useMemo is stabilising an object or array prop — a case people often miss because they are focused on functions:
1// BAD: a new object every render defeats React.memo on <Chart>,
2// even if every field is identical.
3<Chart options={{ theme: "dark", grid: true }} />
4
5// GOOD:
6const options = useMemo(() => ({ theme: "dark", grid: true }), []);
7<Chart options={options} />
8// → same reference on every render, so React.memo bails outThe cost nobody mentions
Memoization is not free, and the way it costs you is counter-intuitive. useCallback does not stop the function from being created.
const handleClick = useCallback(() => save(id), [id]);
// ^^^^^^^^^^^^^^^^^^
// This arrow function is CREATED on every single render, no matter what.
// useCallback only decides whether to RETURN the new one or the cached one.
// → the allocation is not what you saved; the stable identity isSo on every render you pay for: creating the function anyway, storing it in the hook's memory slot, keeping the previous one alive, and comparing every dependency with Object.is. If nothing downstream benefits from the stable reference, you have added four costs and zero savings.
This is 1 of 75+ questions in the React Interview Kit
Get every question with detailed answers, follow-ups and real code — plus coding challenges and a last-minute revision sheet. One-time payment, instant access.
⚡ Get the React Interview Kit → ₹399The three mistakes that show up in interviews
useMemo vs React.memo — they're not alternatives
This trips people up because of the shared name, but they operate at different levels and are usually used together.
| React.memo | useMemo | useCallback | |
|---|---|---|---|
| What it is | A component wrapper (HOC) | A hook | A hook |
| Memoizes | A whole component's render output | A value | A function |
| Used | Around a component definition | Inside a component | Inside a component |
| Compares | Props, shallowly | The dependency array | The dependency array |
React.memo is the consumer; useMemo and useCallback are what make its comparison succeed. React.memo without stable props is useless, and stable props without React.memo are equally useless. You need both halves or neither.
1const Chart = React.memo(function Chart({ data, options, onZoom }) { /* … */ });
2// ^ half 1: skip re-render when props are shallow-equal
3
4function Dashboard({ raw }) {
5 const data = useMemo(() => transform(raw), [raw]); // half 2
6 const options = useMemo(() => ({ theme: "dark" }), []); // half 2
7 const onZoom = useCallback((r) => setRange(r), []); // half 2
8 return <Chart data={data} options={options} onZoom={onZoom} />;
9}
10// → Chart re-renders only when `raw` changes; a parent render alone
11// no longer touches itHow they differ from useEffect
A frequent follow-up, because all three take a dependency array. The difference is when they run and what they are for.
| useMemo / useCallback | useEffect | |
|---|---|---|
| Runs | During render, synchronously | After render, after paint |
| Purpose | Produce a value or function | Perform a side effect |
| Returns | The cached value/function | Nothing (optionally a cleanup) |
| Blocks paint? | Yes — it's part of rendering | No |
| Allowed to | Compute, transform, derive | Fetch, subscribe, log, set timers |
Does React 19's compiler make these obsolete?
Largely, yes — and it is worth saying so in an interview, because it shows you follow where React is going. The React Compiler auto-memoizes components and values at build time, which removes most hand-written useMemo and useCallback calls.
Two caveats worth stating alongside it. Adoption is still partial, so the large majority of production codebases you will join still memoize by hand and will for years. And the compiler relies on your components following the Rules of React — no mutation during render, no side effects in render — so understanding what memoization is doing remains necessary to reason about the code either way.
A decision rule you can actually use
- Am I caching a function? → useCallback. A value? → useMemo.
- Is the consumer wrapped in React.memo, or is this in a dependency array? If no to both → use neither.
- Is this a genuinely expensive computation I have measured? → useMemo, even without a memoized consumer.
- Am I passing an object or array literal as a prop to a memoized child? → useMemo, or you have cancelled out React.memo.
- Still unsure? → leave it out. Unnecessary memoization is easier to add later than to debug now.
Does useCallback improve performance on its own?
No. Without a React.memo child or an effect that depends on the function, it adds cost — the function is still created every render, plus you pay for the dependency comparison and the retained reference — and saves nothing.
What happens if you omit the dependency array entirely?
The hook recomputes on every render, making it equivalent to not using the hook at all — except slower, since you also pay the hook's overhead. This differs from useEffect, where omitting the array also means running after every render.
Can you call useMemo conditionally?
No. Like every hook it must be called at the top level in the same order each render, because React tracks hooks by call index. Put the condition inside the memoized function instead, and return early from there.
How would you prove a memo is actually helping?
React DevTools Profiler: record an interaction, look at which components re-rendered and their timings, add the memo, record again, compare. If the flamegraph looks the same, remove it. "I measured it" is the answer interviewers want — it's the difference between engineering and cargo-culting.
Frequently asked questions
- What is the difference between useMemo and useCallback?
- useMemo runs the function you give it and caches the returned value. useCallback does not run the function — it caches the function itself so the reference stays stable between renders. Underneath they are the same mechanism: useCallback(fn, deps) is exactly useMemo(() => fn, deps).
- When should I use useMemo and useCallback?
- Only when something downstream compares references: you are passing the value or function to a component wrapped in React.memo, or you are using it inside a dependency array. Use useMemo additionally for computations expensive enough that you can measure the difference in the Profiler. Otherwise use neither.
- What's the difference between useCallback and useMemo in practice?
- In practice you reach for useCallback for event handlers passed to memoized children, and useMemo for derived data — filtered or sorted lists — and for object or array literals passed as props. The most common real-world bug is wrapping every handler in useCallback while still passing a fresh object literal in the next prop, which cancels the optimisation entirely.
- Does useCallback improve performance by itself?
- No. If the receiving component is not wrapped in React.memo and the function is not in a dependency array, useCallback is pure overhead: the arrow function is still created on every render, and you additionally pay for storing it and comparing dependencies.
- What is the difference between useMemo, useCallback and useEffect?
- useMemo and useCallback run during render and produce a value or a function. useEffect runs after render and after paint, and performs side effects like fetching or subscribing. All three take a dependency array, but only useEffect is allowed to cause side effects — never put one inside useMemo.
- What is the difference between React.memo and useMemo?
- React.memo is a higher-order component that wraps a component definition and skips re-rendering when its props are shallowly equal. useMemo is a hook used inside a component to cache a single value. They work together: React.memo does the comparing, useMemo and useCallback keep the props stable enough for that comparison to pass.
- Can I use useMemo instead of useCallback?
- Yes — useMemo(() => myFunction, deps) is precisely what useCallback does. useCallback exists purely as more readable shorthand, saving you the double arrow. There is no behavioural difference.
- What is the difference between useState and useMemo?
- useState stores a value you intend to change, and updating it triggers a re-render. useMemo caches a value derived from other values and never triggers a render by itself. If you can compute something from existing props or state, derive it with useMemo (or just inline) rather than duplicating it into state.
- Is it bad to use useCallback everywhere?
- Yes. Each call adds memory for the retained function and a dependency comparison on every render, and it makes components noisier to read. The React team's own guidance is to memoize deliberately, not by default. Blanket memoization is a common code-review flag.
- Why is my useCallback not preventing re-renders?
- Almost always one of three reasons: the child is not wrapped in React.memo, so it re-renders with its parent regardless; another prop on the same element is a new reference each render (a fresh object or array literal); or a dependency in the array changes every render, so the callback is rebuilt anyway.
- Does React 19's compiler replace useMemo and useCallback?
- For the most part, yes — the React Compiler auto-memoizes components and values at build time, removing the need for most manual calls. But adoption is partial, existing codebases will use manual memoization for years, and the compiler depends on your components following the Rules of React, so understanding the mechanism still matters.
- Is useMemo guaranteed to cache the value?
- No. React's documentation describes useMemo as a performance optimisation, not a semantic guarantee — React may discard cached values, for instance to free memory, and recompute on the next render. Never write logic whose correctness depends on the cache persisting; use useRef or useState for that.
This is 1 of 75+ questions in the React Interview Kit
Get every question with detailed answers, follow-ups and real code — plus coding challenges and a last-minute revision sheet. One-time payment, instant access.
⚡ Get the React Interview Kit → ₹399