I thought I understood..
I thought I understood Memoization
The human explanation
Memoization is like writing down the answer to a maths problem so that you don’t have to solve it again, unless the numbers change.
The catch is, writing things down, storing and finding it again, also has a cost. If the problem is simple (1 + 1), it’s often faster to just solve it again.
The technical explanation
Memoization is a general programming technique where you cache the result of a function based on its inputs. If the function is called again with the same inputs, you return the cached result instead of recalculating it.
In JavaScript, this is often implemented by storing previous inputs and outputs in a map or object. The key idea is that for the same input, the output is always the same (a pure function), which makes caching safe.
In React, the useMemo hook applies this idea inside a component. It lets you cache the result of a computation and reuse it across renders. React will only recompute the value if one of the dependencies changes. Otherwise, it returns the previously stored result.
This is useful both for avoiding expensive recalculations and for keeping references stable. In JavaScript, objects and arrays are compared by reference, so even if their contents are the same, creating a new object each render will produce a new reference.
That’s where React.memo comes in. React.memo prevents a component from re-rendering if its props haven’t changed (based on shallow comparison). But if you pass a new object or array each time, the reference changes and the component will still re-render.
Using useMemo to stabilise those values means React.memo can actually do its job. Together, they help avoid unnecessary work, useMemo stabilises values, and React.memo skips re-renders when those values haven’t changed.
So while useMemo is a React hook, the underlying idea is much broader: cache results when inputs are the same, and avoid repeating work unnecessarily.
Why this matters
Without memoization, the same calculations can run on every render, even when nothing has actually changed. Most of the time this isn’t noticeable, but in larger components or with expensive operations (like filtering, sorting, or transforming large datasets), it can start to impact performance.
It also matters for how React decides whether to re-render components. If you create a new object or array on every render, even with the same values, React sees it as different because the reference has changed. This can cause child components to re-render unnecessarily.
By stabilising those values with useMemo, you make it possible for optimisations like React.memo to work properly. This helps avoid wasted renders and keeps your UI more efficient, especially in component trees where props are passed down multiple levels.
So it’s not just about “saving computation”, it’s also about controlling when React thinks something has changed.
The real talk
Most of the time, you don’t need useMemo.
It’s easy to reach for it as a default “performance fix”, but it comes with its own cost. React still has to track dependencies and store the cached value, which can add unnecessary complexity if the calculation is cheap.
In many cases, recalculating a small value is faster and simpler than memoizing it. Overusing useMemo can make code harder to read and give a false sense of optimisation.
It’s most useful when, 1) the computation is genuinely expensive or 2) you need to keep object/array references stable for memoized children (React.memo)
It’s also worth noting that with things like the React Compiler, some of this may become less manual over time. The direction React is heading suggests that many of these optimisations could be handled automatically, which makes overusing hooks like useMemo even less necessary.
So while the idea of “caching” sounds appealing, the real skill is knowing when the extra complexity is actually worth it.