Prompt
Create a list where each item is rendered by a component wrapped in React.memo. Verify that items don't re-render when their props haven't changed (add a console.log in the child).
Solution
React.memo exists because React's default is brutal: when a parent renders, every child re-renders, props changed or not. Memo converts that O(subtree) cost into a shallow prop comparison — here, pressing "+" re-renders List, but each MemoListItem sees the same label (compared with Object.is) and skips. The console.log in the child is your proof: it fires once per item on mount, then stays silent while the counter climbs.
Two placement details matter. The wrap happens at module scope — React.memo(ListItem) inside List's body would create a new component type every render, forcing a remount of every row, the exact opposite of the goal. And the memo only holds because the props are primitives: strings compare equal by value under Object.is. Pass an inline object, array, or arrow function and it's a new reference every render — the memo silently never hits, and you pay the comparison plus the render. That's why memo travels with useCallback/useMemo on the props feeding it.
Memo can also hurt: for cheap components the comparison costs more than re-rendering. Profile first (React DevTools Profiler), then memoize proven hot paths — typically list rows and expensive visualizations. The React Compiler automates exactly this, which tells you where the ecosystem is heading.
Red flag: "Wrap everything in memo to be safe." Blanket memoization adds overhead and hides the real problem — usually unstable references or state living too high in the tree.
Say it: "memo is a shallow reference comparison, so it only pays off when the props feeding it are referentially stable — I apply it to profiled hot paths like list rows, not everywhere by default."