The Codex / Technologies / React / Performance (memo/useMemo/useCallback)

Performance (memo/useMemo/useCallback)

When and why to optimize React renders - React.memo, useMemo, useCallback, and the golden rule: measure before optimizing.

What it is, in plain English: React re-renders are usually cheap, so most components need no optimization at all. When a specific component IS measurably slow (expensive calculations, large lists), three tools help: React.memo skips re-rendering a component if its props haven't changed. useMemo caches the result of an expensive calculation between renders. useCallback caches a function reference so it doesn't change on every render -- important when passing callbacks to memoized children.

Most components don't need optimization

The golden rule: measure first. React's default re-rendering is fast. Adding memo/useMemo/useCallback everywhere "just in case" adds complexity and can actually make things SLOWER (memoization itself has a cost). Only optimize a component after you've measured it's actually slow.

React.memo -- skip re-rendering if props are unchanged

// Without memo: ExpensiveChild re-renders every time Parent re-renders,
// even if 'data' never changed:
function ExpensiveChild({ data }) {
  console.log("Rendering...");   // logs every time Parent re-renders
  return <div>{data}</div>;
}

// With memo: only re-renders if 'data' actually changed:
const ExpensiveChild = React.memo(function ExpensiveChild({ data }) {
  console.log("Rendering...");   // only logs when 'data' actually changes
  return <div>{data}</div>;
});

function Parent() {
  const [unrelatedState, setUnrelatedState] = React.useState(0);
  return (
    <div>
      <button onClick={() => setUnrelatedState(s => s + 1)}>Click</button>
      <ExpensiveChild data="constant" />
      {/* memo() skips re-rendering ExpensiveChild — its props never change */}
    </div>
  );
}

useMemo -- cache an expensive calculation's result

function ProductList({ products }) {
  // Without useMemo: this runs on EVERY render, even unrelated ones:
  const sorted = products.slice().sort((a, b) => b.price - a.price);

  // With useMemo: only re-sorts when 'products' actually changes:
  const sortedMemo = React.useMemo(() => {
    return products.slice().sort((a, b) => b.price - a.price);
  }, [products]);

  return <ul>{sortedMemo.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}
// Use useMemo for calculations that are genuinely expensive --
// sorting/filtering large arrays, complex math. NOT for simple operations.

useCallback -- cache a function reference

function Parent() {
  // Without useCallback: a NEW function is created every render:
  const handleClick = () => console.log("clicked");

  // With useCallback: the SAME function reference, unless deps change:
  const handleClickMemo = React.useCallback(() => {
    console.log("clicked");
  }, []);  // never changes

  return <MemoizedChild onClick={handleClickMemo} />;
  // Why this matters: MemoizedChild is React.memo'd, but a new onClick
  // function every render would defeat that memo (new reference = "changed prop")
}
Try It Yourself (JSX)

Official Sources

The Codex links only to official documentation. No third-party tutorials.