# useMemo

## 1. The Basic API

`useMemo` caches the *result* of an expensive calculation across re-renders, recomputing it only when one of its listed dependencies has changed:

```tsx
import { useMemo } from "react";

function ProductList({ products, query }: { products: Product[]; query: string }) {
  const filtered = useMemo(() => {
    console.log("filtering..."); // only logs when products or query actually change
    return products.filter((p) => p.name.toLowerCase().includes(query.toLowerCase()));
  }, [products, query]);

  return <ul>{filtered.map((p) => <li key={p.id}>{p.name}</li>)}</ul>;
}
```

Without `useMemo`, `products.filter(...)` re-runs on *every* render of `ProductList` — including renders triggered by completely unrelated state changes (a parent re-rendering, a sibling's click handler, an unrelated `useState` in this same component). `useMemo` skips the recomputation and hands back the previously cached array as long as `products` and `query` are reference-equal (via `Object.is`) to what they were last time.

## 2. What Actually Gets Compared

The dependency array is compared **element by element**, using the same `Object.is` semantics as every other hook's dependency array — not deep equality:

```tsx
function Dashboard({ userId }: { userId: string }) {
  // BAD: a new object literal is created every render, so this dependency
  // is NEVER equal to the previous one, and useMemo recomputes every time —
  // all the caching benefit is silently lost
  const filters = useMemo(() => ({ userId, active: true }), [{ userId }]);
  //                                                            ^^^^^^^^ new object every render

  // GOOD: depend on the primitive values that actually determine the result
  const summary = useMemo(() => computeSummary(userId, true), [userId]);
}
```

Passing an object or array literal *inline as a dependency* defeats the whole point — it is never `===` to the previous render's literal. Depend on the primitive values (or on a stable reference produced by another `useMemo`/`useCallback`) instead.

## 3. useMemo Is Not Guaranteed to Skip Work

React's docs are explicit that `useMemo` is a **performance hint**, not a semantic guarantee. React *may* discard a cached value and recompute it anyway — for instance, to free memory for offscreen components, or in future React versions that experiment with recomputing memoized values to prep for features like pre-warming component trees:

```tsx
// This pattern is WRONG: it relies on useMemo to guarantee an object is only
// created once, using that assumption for correctness rather than performance
const cache = useMemo(() => new Map(), []); // do NOT rely on this Map surviving

// If correctness (not just performance) requires "only created once," use
// useRef or module state instead — useMemo alone doesn't promise it
const cacheRef = useRef<Map<string, number>>();
if (!cacheRef.current) cacheRef.current = new Map();
```

The rule: write code that is *correct* even if `useMemo` recomputed on every render (i.e., as if it were plain inline code); use it purely to make that already-correct code faster.

## 4. When useMemo Actually Helps

Three situations justify reaching for it:

```tsx
// 1. A genuinely expensive computation (sorting/filtering large arrays, heavy math)
const sorted = useMemo(() => [...hugeArray].sort(compareFn), [hugeArray, compareFn]);

// 2. Preserving reference stability for a value passed to a memoized child
//    (React.memo) so that child doesn't re-render just because a new object
//    literal was created this render
const config = useMemo(() => ({ theme, locale }), [theme, locale]);
return <MemoizedPanel config={config} />;

// 3. Preserving reference stability for a value used as ANOTHER hook's dependency
const chartData = useMemo(() => transformForChart(rawData), [rawData]);
useEffect(() => {
  renderChart(chartData); // stable dependency avoids re-running the effect every render
}, [chartData]);
```

For anything cheap — string concatenation, simple arithmetic, a `.map()` over a handful of items — the overhead of running `useMemo` itself (storing the array, doing the comparison) usually costs more than just recomputing the value plainly. Don't wrap every computation in `useMemo` "just in case."

## 5. useMemo vs Computing Inline

The default should be to compute values inline during render, not to reach for `useMemo` first:

```tsx
function Greeting({ firstName, lastName }: { firstName: string; lastName: string }) {
  // No useMemo needed — string concatenation is essentially free
  const fullName = `${firstName} ${lastName}`;
  return <h1>Hello, {fullName}</h1>;
}
```

`useMemo` is an *opt-in* optimization for measured or clearly expensive work, not a default wrapper to apply everywhere "for performance." Reach for it after profiling shows a real cost, or when reference stability is specifically required by a downstream `React.memo` or dependency array — see the Performance optimization topic for the broader picture of when memoization actually pays off.

![useMemo's hook node stores the last dependency array and the cached value; on the next render React compares each element with Object.is before deciding to recompute](diagrams/01-usememo-cache-check.svg)

## 6. Under the Hood

React does not attach state to the component *function* — a function component is just a function, called fresh every render with no persistent memory of its own. Hook data has to live somewhere else: it lives on the **fiber**, React's internal object representing one component instance in the tree.

Each fiber holds a `memoizedState` pointer to the head of a **singly linked list of hook objects**, one node per hook call, built in the exact order those hooks are called and walked in that same order on every subsequent render. Matching is done purely by **call order**, which is why `useMemo` (like every hook) cannot be called conditionally — doing so shifts every later hook call to the wrong node in the list.

A `useMemo` hook node stores exactly two things in its `memoizedState` slot: the **cached value** from the last time the factory function ran, and the **dependency array** from that same run. When the component re-renders and execution reaches this `useMemo` call again, React does not simply call the factory — it first walks the new dependency array against the stored one, comparing each element pairwise with `Object.is` (the same identity comparison `useEffect` and `useCallback` use, exact for primitives, reference-only for objects/arrays/functions). If every element compares equal, React skips calling the factory entirely and returns the value already sitting in the node. If any element differs (or the array's length/identity changed, or this is the first render), React calls the factory function now, during this render, and overwrites the node's cached value and dependency array with the new results before returning the freshly computed value.

This is the load-bearing detail that separates `useMemo` from merely "caching": the factory function, when it does run, runs synchronously as part of the current render — not in an effect, not asynchronously. That's what makes it safe to use for values needed to *render* the current output (unlike `useEffect`, which always runs after commit). It's also why `useMemo`'s optimization is fundamentally about **avoiding redundant work across renders that don't need it**, not about deferring work: on any render where dependencies actually changed, the cost is paid in full, in that render, exactly like it would be without `useMemo` at all.

React does not promise that a cached `memoizedState` slot survives forever. Internally, React may discard memoized values for fibers it needs to reclaim memory from (for example, a component tree kept offscreen by Suspense/Activity boundaries), and the React team has explicitly reserved the right to make `useMemo` "not guarantee" recomputation-skipping in future versions to enable upcoming optimizations. That reservation is exactly why component logic must be written so it stays *correct* if the factory reran on every single render — `useMemo` may only ever be trusted to make already-correct code *faster*, never to make otherwise-incorrect code work.

## 7. Common Pitfalls

1. **Passing an inline object/array literal as a dependency** — a new literal is created every render, so it's never `Object.is`-equal to the previous one, and the memoization never hits; depend on the primitive fields instead, or memoize the object itself upstream.
2. **Relying on `useMemo` for correctness** (e.g., "this only creates the Map once") rather than performance — React does not guarantee the cached value survives; use `useRef` or module-level state for anything that must genuinely persist exactly once.
3. **Wrapping cheap computations in `useMemo`** — the bookkeeping (storing the array, running the `Object.is` comparisons) can cost more than just recomputing a trivial value inline; reserve it for measured, non-trivial cost.
4. **Forgetting a dependency the factory function closes over** — just like `useEffect`, the factory closes over render-scope variables (see the closures material in `languages/javascript`); an incomplete dependency array means the memoized value can silently keep using a stale prior value instead of recomputing.
5. **Expecting `useMemo` to prevent a child from re-rendering** by itself — reference stability from `useMemo` only helps if the child is *also* wrapped in `React.memo`; without that, the child re-renders regardless of whether its props are the same reference.

## 8. Quick Reference

```
const value = useMemo(() => computeExpensive(a, b), [a, b])
  → recomputes only when a or b changes (Object.is per element)
  → returns the SAME cached reference otherwise

Use when:  expensive computation, OR reference stability needed for
           React.memo children or another hook's dependency array
Skip when: computation is cheap — just compute inline

NOT a correctness guarantee — React may discard the cache and recompute
anyway; write code that stays correct even if that happens.
```
