Frontend Master

JavaScript Curriculum

Performance — memo & callbacks
+60 XP

Performance — memo & callbacks

medium
~30 min·60 XP

The coffee shop menu renders 200 items. Every cart update causes all 200 MenuCards to re-render — even though none of their props changed. React.memo plus useCallback solves this with a few lines.

Performance — memo & callbacks

React is fast by default. Memoisation tools help you skip unnecessary re-renders — but only apply them after profiling confirms there is a measurable problem.

React.memo — Skip Component Re-renders

See exactly which components re-render with and without memo:

memo-visualizer.jsx
App
0
Header
0
MenuCard
memo ✓0
CartBadge
memo ✓0
// ✓ React.memo — skips re-render if props unchanged
const MenuCard = memo(function MenuCard({ name, price }) {
  return <div>{name} — £{price.toFixed(2)}</div>
})
// MenuCard won't re-render when count or text changes
// Only re-renders when name or price props change
ℹ️React.memo uses shallow comparison
By default, React.memo compares props shallowly — same references, not deep equality. An object prop `{ name: 'Espresso' }` is a new reference every render, so memo won't help unless you stabilise it with useMemo. A primitive like a string or number is compared by value — memo works fine.

useCallback — Stable Function References

Without useCallback, every render creates a new function — breaking React.memo:

useCallback.jsx
// ✓ With useCallback — stable function reference
function App() {
  const [cart, setCart] = useState([])

  // Stable reference — only changes if deps change:
  const handleOrder = useCallback(
    (id) => setCart(c => [...c, id]),
    []  // no deps — stable for component lifetime
  )

  // React.memo works correctly now:
  return <MenuCard onOrder={handleOrder} />
}
Live demo
Espresso
Cappuccino
Cart: 0 items
Handler log
// Interact to see handler identity
💡useCallback is useMemo for functions
`useCallback(fn, deps)` is exactly equivalent to `useMemo(() => fn, deps)`. It returns the same function reference across renders unless deps change. Only useful when the function is passed to a memoized child — otherwise it adds overhead with no benefit.

When to Use Each — and When Not To

React.memo, useMemo, useCallback — the right tool for each job:

render-profiler.jsx
// React is fast by default — DON'T optimise prematurely

// Profile FIRST, then optimise:
// 1. Use React DevTools Profiler to find slow components
// 2. Check if unnecessary re-renders cause measurable lag
// 3. Only then reach for memo/useMemo/useCallback

// The cost of premature optimisation:
// - memo: adds comparison cost on every render
// - useMemo/useCallback: adds closure allocation cost
// For most components, the overhead > the savings

// Good candidates for optimisation:
// ✓ Long lists (50+ items) with expensive renders
// ✓ Heavy computations (chart data, search filtering)
// ✓ Components that receive callback props from parents
// ✓ Components that show in profiler as hot spots

// Bad candidates:
// ✗ Simple components that render fast anyway
// ✗ Components that almost always re-render on prop changes
// ✗ Top-level components (they rarely cause the problem)
⚠️Profile before you optimise
Every memoisation adds a comparison cost. For small components, this overhead exceeds the savings. Use React DevTools Profiler to identify genuinely slow components before adding memo. The rule: make it work, make it right, then make it fast.

Your Challenge

Build a list of 50 MenuCard items. Add a text input whose state change causes the parent to re-render. Without memo: all 50 cards re-render on each keystroke. Add React.memo to MenuCard and useCallback to the onOrder handler — verify only the parent re-renders on keystroke.

Challenge

Wrap a MenuCard in React.memo. Stabilise its onOrder callback with useCallback in the parent. Measure: without these, how many renders happen on a cart update? With them, how many? Use the render counter pattern to count.

React.memouseMemouseCallbackmemoizationre-rendershallow-comparisonrender-profilingperformancepremature-optimisation
Performance — memo & callbacks | Nexus Learn