back

Memoization in React: when it helps and when it just adds noise

There's a phase most React developers go through where they start wrapping everything in useMemo and useCallback. It feels responsible — caching things, preventing re-renders, being thoughtful about performance. But memoization has a cost too: memory, code complexity, and the cognitive overhead of tracking dependencies correctly.

At one point we had a filter panel component — a dozen checkboxes and a search input — where every handler was wrapped in useCallback and every derived value in useMemo. It looked thorough. When I profiled it with React DevTools Profiler, the component was re-rendering maybe twice per interaction and each render took under 1ms. We were paying the memoization overhead for a component that had no performance problem to begin with. Removing it cut about 30 lines of dependency arrays and made the code straightforward to read again. The lesson wasn't that memoization is bad — it's that measuring first changes everything.

The real question is whether the computation or reference is actually expensive. Primitive comparisons are cheap. Object creation is cheap. What's expensive is re-rendering a complex tree, or recalculating something that touches hundreds of items on every keystroke. Memoization makes sense when you can measure the problem it solves — not as a default style.

With the React Compiler now stable in React 19, a lot of manual memoization will become unnecessary — the compiler handles referential stability automatically. The best thing you can do now is understand the primitives well enough to recognize where the compiler still needs your help.