Chapter 1 of 4

Measure Before Optimising

Why most memoisation is wasted, and how to find the real problem.

React is fast. Most components render in well under a millisecond, and wrapping them in memo costs a comparison plus memory while saving nothing. Optimisation without measurement usually makes code harder to read and no faster.

The React DevTools Profiler

  1. Open React DevTools and switch to the Profiler tab.
  2. Press record, perform the slow interaction, press stop.
  3. The flamegraph shows every component that rendered and how long it took.
  4. Turn on 'Record why each component rendered' in the settings - it names the prop or state that caused it.

You are looking for two things: a component that renders far more often than it should, and a component that takes a long time on a single render. They have different fixes.

The Profiler component

For measurements in code - in tests, or reported to analytics - wrap a subtree in <Profiler>.

import { Profiler } from "react";

function onRender(id, phase, actualDuration) {
  // phase is "mount" or "update"
  console.log(id, phase, actualDuration);
}

<Profiler id="ProductList" onRender={onRender}>
  <ProductList products={products} />
</Profiler>

Rendering is often not the bottleneck

  • A large JavaScript bundle delays the first render more than any component ever will.
  • Unoptimised images usually dominate real-world page load.
  • A slow API request cannot be memoised away.
  • A layout thrash - reading and writing the DOM in a loop - can be far slower than any React work.