What’s the difference between useMemo and useCallback, and when does each actually matter for performance?

Cognizant React Developer 1–3 Years React

useMemo caches a computed value between renders, recomputing it only when its dependencies change. useCallback does the same thing but specifically for function references, returning the same function identity across renders unless dependencies change. They solve the same underlying problem (avoiding unnecessary work), just for values versus functions.

When they actually matter

Neither helps just because a computation “feels expensive.” They matter in two specific situations: when a value or function is passed as a prop to a child wrapped in React.memo, since a new function or object reference on every render defeats memoization entirely, causing the child to re-render anyway; or when a value is genuinely expensive to compute (sorting a large list, heavy math) and you want to skip that work on renders where the inputs haven’t changed.

Best practice

Don’t wrap every function in useCallback and every value in useMemo by default. Each one has its own overhead (storing the cached value, comparing dependencies), and for cheap computations or components that don’t re-render often, that overhead can exceed the cost of just recomputing. Reach for them deliberately: profile first, or apply them specifically at the boundary where a prop is passed into a memoized child.

Edge case interviewers probe for

Ask what happens if the dependency array for a useCallback‘d function is wrong, missing a variable it actually closes over. The function will keep referencing a stale value from an earlier render, a subtle bug that’s harder to spot than a simple missing re-render, since the code still runs, just with outdated data.

Common mistake

Wrapping a function in useCallback without wrapping the child component in React.memo, which accomplishes nothing, the child re-renders on every parent render regardless of whether the function reference is stable, since React.memo is the part that actually skips re-rendering.

What the interviewer is checking

Whether you understand these as tools for a specific problem (breaking memoization via reference equality) rather than generic “performance” hooks to sprinkle everywhere.

Imagine you photocopy a document fresh every single time someone asks for it, even if nothing in the document changed since the last copy. useMemo is like keeping the last copy on hand and only making a new one if the content actually changed, saving the copying work when nothing’s different.

useCallback does the same thing, but for a specific kind of thing: a set of instructions (a function) instead of a document. Without it, you hand someone a brand new set of instructions every time, even if the instructions themselves didn’t change at all, just freshly rewritten. If that person only pays attention when they get a genuinely NEW set of instructions (like a component using React.memo that skips re-rendering when props haven’t changed), handing them a freshly-rewritten-but-identical copy every time tricks them into thinking something’s new when it isn’t.

Why interviewers ask this

These are among the most over-used and misunderstood React hooks. This checks whether a candidate applies them deliberately or cargo-cults them everywhere.

What a strong answer signals

That you understand reference equality and React.memo’s shallow comparison, and only reach for these hooks where that specifically matters.

Common follow-ups

  • What does React.memo actually compare?
  • What happens with a missing dependency in the array?
  • When would useMemo actually make performance worse?

Advanced variation

“How would you profile whether a memoization is actually helping,” which expects using React DevTools’ profiler to measure render counts and durations rather than assuming.

A dashboard with a large data table wrapped in React.memo kept re-rendering on every keystroke in an unrelated search box, despite the table’s actual data not changing. The parent component was recreating an onRowClick callback function on every render, a fresh reference each time, which broke the table’s memoization since React.memo‘s comparison saw a “new” prop every time. Wrapping that callback in useCallback with the correct dependencies stopped the unnecessary re-renders, cutting keystroke-to-paint latency noticeably on a table with a few thousand rows.

DataTable.jsx
const Row = React.memo(function Row({ item, onClick }) {
  // Only re-renders when `item` or `onClick` actually change identity.
  return <tr onClick={() => onClick(item.id)}>{item.name}</tr>;
});

function Dashboard({ items }) {
  const [search, setSearch] = useState('');

  // Without useCallback, this is a NEW function on every keystroke,
  // breaking Row's memoization even though `items` never changed.
  const handleRowClick = useCallback((id) => {
    console.log('clicked', id);
  }, []);

  return (
    <>
      <input value={search} onChange={(e) => setSearch(e.target.value)} />
      {items.map((item) => (
        <Row key={item.id} item={item} onClick={handleRowClick} />
      ))}
    </>
  );
}
  1. 1useMemo caches a computed value; useCallback caches a function reference.
  2. 2They only matter when paired with React.memo on a child, or for genuinely expensive computations.
  3. 3Wrapping everything in these hooks by default adds overhead without benefit.
  4. 4A wrong or missing dependency causes stale closures, a subtle bug distinct from a missed re-render.
  5. 5Profile with React DevTools before assuming a memoization is actually helping.