Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Effects, Refs and Reuse  ›  Lesson

useMemo and useCallback — Caching Between Renders

useMemo 15 minuseMemo caches a calculated value and useCallback caches a function between renders, recomputing only when their dependencies change; both are performance tools, not correctness tools.

useMemo and useCallback — Caching Between Renders

Your reading list grew to 5,000 books, and each render sorts them all by title — even when the only thing that changed was the dark-mode toggle. Rendering is usually fast enough not to care, but when one calculation is genuinely slow, React lets you keep the last answer until its inputs change.

The rule

Cache the result; recompute only when a dependency changes.

useMemo caches a value.

import { useMemo } from 'react';

function BookList({ books, theme }) {
  const sorted = useMemo(
    () => [...books].sort((a, b) => a.title.localeCompare(b.title)),
    [books]
  );
  return <ul className={theme}>{sorted.map(b => <li key={b.id}>{b.title}</li>)}</ul>;
}

On the first render React runs the function and stores the result. On later renders it compares each dependency with the previous render's; if none changed, it hands back the stored result without calling the function. When theme changes, books is the same array, so the sort is skipped. (Note the [...books] copy: sort mutates, and props must not be changed.)

useCallback caches a function.

const handleRemove = useCallback(
  id => dispatch({ type: 'removed', id }),
  []
);

Every render normally creates brand-new function objects. That matters when a child is wrapped in memo (which skips re-rendering if its props are unchanged) or when a function is an effect dependency — a new function each time looks like a change. useCallback(fn, deps) is the same as useMemo(() => fn, deps): it keeps the same function until a dependency changes.

Performance, not correctness. React may occasionally throw a cache away, so your code must still work if the value is recomputed. And caching has its own cost: comparing dependencies and holding memory. The newer React Compiler can add this memoisation automatically, so many projects need fewer hand-written calls.

The shortcut

Before memoising, ask: "Is it slow, or does someone compare it?"

  • Slow calculation that repeats with the same inputs → useMemo.
  • Function passed to a memo child or listed in an effect's dependencies → useCallback.
  • Neither → write plain code.
Toggling the theme with a cached sortStep 1 of 4
  1. First render

    useMemo runs the sort on 5,000 books and stores the result with [books].

Drills

Answer fast — trust the rule.

quick drill

What does useMemo(() => compute(a), [a]) return on a render where a has not changed?

quick drill

Which is equivalent to useCallback(fn, [x])?

quick drill

A memo-wrapped BookCard still re-renders on every parent render. Its props are a book object and onRemove={() => remove(id)}. What is the likely cause?

quick drill

Is it worth wrapping const full = first + ' ' + last in useMemo?

Remember

  • Cache the result; recompute only when a dependency changes.
  • Slow, or compared? Only then reach for useMemo or useCallback.
  • Trap: treating memoisation as required for correctness, or sprinkling it everywhere.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainWhen to cache

BookList sorts 5,000 books by title on every render, even when only an unrelated theme toggle changed. It also passes onRemove to a BookCard wrapped in memo, which still re-renders every time. Explain how useMemo and useCallback help each problem, and when you would not bother with them.