useMemo

Cache an Expensive Computation Until Its Inputs Change

useMemo

`useMemo` remembers the result of a computation and skips re-doing it on later renders if the inputs haven't changed.

4 min read Level 2/5 #react#useMemo#hooks
What you'll learn
  • Memoize a heavy computation
  • Memoize an object/array to keep its reference stable
  • Recognize when memoization is NOT worth it

useMemo(fn, deps) runs fn, remembers its return value, and only re-runs fn when one of deps changes. Same shape as useEffect, but it produces a value instead of running a side effect.

Memoizing Heavy Work

function Search({ items, query }) {
  const filtered = useMemo(() => {
    return items.filter(item =>
      item.name.toLowerCase().includes(query.toLowerCase())
    );
  }, [items, query]);

  return <List items={filtered} />;
}

Without useMemo, the filter runs every render. With it, the filter runs only when items or query change.

Stabilizing Object/Array References

The second use case: keep a value’s identity stable across renders so a downstream comparison (memo, effect deps) doesn’t see it as new.

function Page({ id }) {
  // ✗ New object every render — Child sees a "different" prop every time
  return <Child config={{ id, mode: "live" }} />;
}

function Page({ id }) {
  // ✓ Same object until id changes
  const config = useMemo(() => ({ id, mode: "live" }), [id]);
  return <Child config={config} />;
}

This usually only matters when Child is wrapped in memo (see the Patterns chapter) or compares its props somewhere expensive.

Deps Follow The Same Rules As useEffect

Everything reactive that fn reads should be in deps. The ESLint rule will tell you if you forget.

const greeting = useMemo(() => `Hi, ${name}!`, [name]);

When Memoization Is Worth It

  • The computation is genuinely slow (sorting/filtering many thousands of items, complex regex, JSON parsing)
  • A child wrapped in memo should skip when only unrelated state changes in the parent
  • An effect or another memo lists this value in deps and you don’t want it to re-run constantly

When it’s NOT worth it:

  • Most light computations (a + b, simple .map)
  • Tiny arrays
  • “Just in case” — the overhead can exceed the savings

The Mental Model

useMemo is a cache keyed by deps. Same deps → same cached result. Different deps → recompute.

Up Next

Same idea for functions: keep them from being created brand-new on every render.

useCallback →