# memo, useMemo and useCallback — React

Source: https://www.skillbyai.com/en/react/p-memo

> Skip work when inputs have not changed.

## Stable references enable skipping

When a parent re-renders, its children re-render by default. `memo(Component)` skips re-rendering when props are shallowly equal. But a new function or object created on every render is a new reference, so it defeats `memo`. `useCallback` keeps a function stable and `useMemo` keeps a computed value stable between renders while dependencies are unchanged. Measure first with the React DevTools Profiler; the React Compiler can add much of this memoisation automatically where it is enabled.

## Fewer wasted renders, fewer surprises

Memoisation, keys and error boundaries keep apps fast and resilient.

![Three ideas: memoisation, keys and identity, error boundaries.](assets/figures/react/section-7-map.svg) — Figure 7.1 — Memoisation, keys and error boundaries.

## Render counts with and without stable props, run

I ran this with React 19.3 and react-dom 19.3 on Node.js 22, compiling JSX with esbuild 0.28. Browser-style examples use jsdom 29 as a fake DOM (imported from a small setup-dom.mjs file) and act() to apply updates. After the initial render and three parent re-renders: a plain child renders 4 times, a memo child given a new inline function each time also renders 4 times, and a memo child given useCallback and useMemo values renders only once.

```jsx
import "./setup-dom.mjs";   // test setup: jsdom provides a browser-like DOM in Node
import { useState, useCallback, useMemo, memo, act } from "react";
import { createRoot } from "react-dom/client";

const renders = { plain: 0, memoBadProp: 0, memoGood: 0 };
const Plain = ({ onPick }) => { renders.plain++; return <i />; };
const MemoBad = memo(({ onPick }) => { renders.memoBadProp++; return <i />; });
const MemoGood = memo(({ onPick, items }) => { renders.memoGood++; return <i />; });
function Parent() {
  const [n, setN] = useState(0);
  const inline = () => {};                               // new function every render
  const stable = useCallback(() => {}, []);              // same function across renders
  const items = useMemo(() => [1, 2, 3], []);            // same array across renders
  return (<>
    <button onClick={() => setN(n + 1)}>{n}</button>
    <Plain onPick={stable} /><MemoBad onPick={inline} /><MemoGood onPick={stable} items={items} />
  </>);
}
const root = createRoot(document.getElementById("root"));
await act(() => root.render(<Parent />));
for (let i = 0; i < 3; i++) await act(() => document.querySelector("button").click());
console.log(renders);
```

Output:

```
{ plain: 4, memoBadProp: 4, memoGood: 1 }
```

## Profile before optimising

Most re-renders are cheap; add memoisation where the Profiler shows real cost.

**Quiz:** Why does memo not help when you pass an inline arrow function as a prop?

- [ ] memo ignores functions
- [x] A new function is created each render, so props are never equal
- [ ] Arrow functions cannot be props
- [ ] memo only works on classes

*Answer:* A new function is created each render, so props are never equal. Stabilise references with useCallback.
