The Over-Optimization Trap

Why wrapping everything in useMemo and React.memo actually makes your app slower, and when you should actually optimize.

React6 min readConcept 32 of 46

The Myth of Free Performance

A common beginner mistake is to think: 'If re-renders are bad, I should just wrap every component in React.memo and every function in useCallback to prevent them.'

This is a massive trap. Caching is not free. When you use React.memo or useMemo, you are asking React to do *extra work* on every render.

Specifically, React has to allocate memory for the cache, iterate through the dependency array, and perform shallow equality checks (===) on every single prop.

The Cost of Caching

React is incredibly fast at calling functions (which is all a render is) and diffing the Virtual DOM.

For a simple button component, checking oldProps.onClick === newProps.onClick takes *more* CPU time than just letting the button re-render in the Virtual DOM.

If you 'over-optimize', you are taxing the CPU on every single render to evaluate caches for components that are perfectly fast to just re-render.

The Rule of Thumb

**Do NOT optimize by default.** Write your code without useMemo, useCallback, or React.memo first.

Only introduce them if:

1. You have measured a performance issue (using the React Profiler).

2. The component is genuinely expensive to render (e.g., a massive data table, an interactive chart).

3. You need to preserve referential equality to prevent a *downstream* expensive component from re-rendering.

The Cost of the Check

Interact with the diagram below. We have a 'Simple Component'. See what happens when we let it re-render normally versus forcing React to check its memoization cache on every keystroke.

// ✅ FAST (NO OPTIMIZATION) function App() { const [text, setText] = useState(""); return ( <div> <input onChange={e => setText(e.target.value)} /> <SimpleText text={text} /> </div> ); } // React skips the expensive cache checks and just // blazes through the render. It's much faster! function SimpleText({ text }) { return <p>{text}</p>; }
Parentstate: ""
FAST TRACKSkipping checks
CPU TAXChecking: prevProps === nextProps?
(Takes longer than just rendering!)
SimpleTextprop: ""

Type in the input. Without memoization, the child renders instantly. WITH memoization, React has to run a 'Prop Evaluator' on every keystroke, taxing the CPU before doing the exact same render it was going to do anyway! Caching isn't free.

Check yourself

Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.

  1. 1Why shouldn't you wrap a simple `<Button text="Click me" />` component in `React.memo`?
  2. 2If a component is wrapped in `React.memo`, which of these props will break the memoization and cause a re-render?
  3. 3What is usually the best alternative to using `React.memo` to fix a performance issue?

Remember this

  • Caching is not free: useMemo and React.memo have a CPU and memory cost.
  • Do not proactively optimize. Measure first using the React Profiler.
  • Only memoize genuinely expensive components (Data Grids, Charts, complex SVG trees).
  • A single inline object {} or function () => {} prop will instantly break React.memo.
  • Move state down (colocation) before reaching for memoization.

Done with this concept?

Mark it complete to track your progress. No login needed.