React Render Optimization Patterns
The question "How do you optimize React rendering?" is deceptively simple. The answer reveals your depth.
Understanding When React Re-renders
A component re-renders when:
- Its state changes
- Its parent re-renders (props may or may not have changed)
- The context it consumes changes
React does NOT re-render because:
- A DOM event fires
- A ref changes
- A variable outside state changes
The Optimization Hierarchy
Level 1: Move State Down
The cheapest optimization â co-locate state with the component that uses it.
// Before: entire page re-renders on hover
function Page() {
const [hovered, setHovered] = useState(false);
return (
<div>
<ExpensiveHeader />
<HoverCard onHover={setHovered} hovered={hovered} />
<ExpensiveFooter />
</div>
);
}
// After: only HoverCard re-renders
function Page() {
return (
<div>
<ExpensiveHeader />
<HoverCard />
<ExpensiveFooter />
</div>
);
}Level 2: Composition â Children as Props
function ScrollTracker({ children }) {
const [scrollY, setScrollY] = useState(0);
useEffect(() => {
const handler = () => setScrollY(window.scrollY);
window.addEventListener('scroll', handler);
return () => window.removeEventListener('scroll', handler);
}, []);
return (
<div>
<ScrollIndicator position={scrollY} />
{children}
</div>
);
}children was created by the parent, so it won't re-render when scrollY changes.
Level 3: React.memo (Use Sparingly)
const ExpensiveList = React.memo(function ExpensiveList({ items }) {
return items.map(item => <ListItem key={item.id} {...item} />);
});Only use when the component is expensive AND its parent re-renders frequently with unchanged props.
Level 4: useMemo / useCallback
const sortedItems = useMemo(
() => items.sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
const handleClick = useCallback(
(id) => dispatch({ type: 'SELECT', id }),
[dispatch]
);The Anti-Patterns
- Memoizing everything â adds complexity, memory overhead, and often doesn't help
- Optimizing without measuring â use React DevTools Profiler first
- Fixing symptoms not causes â a component re-rendering 100 times means the architecture is wrong
The senior approach: design component boundaries so re-renders are naturally scoped. Memoization is a last resort, not a first tool.