Wrapping Components with React.memo
Practice wrapping components with React.memo and watching which re-renders disappear -- and which ones don't.
Reading about memoization is one thing. Watching the render highlights and seeing which components stop flashing -- that's when it actually clicks.
This exercise puts you in a realistic scenario: a parent component with multiple children, some of which need their re-renders reduced. Your job is to apply React.memo, useCallback, and useMemo in the right places and watch the behavior change.
What You'll Practice
The setup is a dashboard with a filter bar and a list of cards. The filter state lives in the parent. Every time it changes, the whole tree re-renders -- including cards that don't care about the filter at all.
The problem: Some components re-render on every filter change even when their props haven't changed. The re-render highlights make this obvious.
The goal: Eliminate the unnecessary re-renders by applying memoization correctly -- but only where it makes a measurable difference.
Key Things to Notice
As you work through the exercise, pay attention to a few patterns:
React.memo alone isn't enough for function props. If you wrap a child in memo but the parent passes an inline function, the function reference changes every render → memo's comparison fails → child still re-renders.
The fix requires two pieces working together. memo on the child + useCallback for the function prop passed to it. Neither works without the other in this case.
Not every component needs to be memoized. Some re-renders are fast and expected. The goal isn't zero re-renders -- it's eliminating renders where the output can't possibly have changed.
The Pattern at a Glance
// Before -- FilterCard re-renders on every parent state change
function Dashboard() {
const [filter, setFilter] = useState('all');
const handleChange = (value) => setFilter(value); // new fn every render
return (
<>
<FilterBar onChange={handleChange} /> {/* re-renders even if filter UI hasn't changed */}
<StatsSummary /> {/* doesn't use filter -- but still re-renders */}
</>
);
}
// After -- only the components that actually changed update
const FilterBar = memo(function FilterBar({ onChange }) { /* ... */ });
const StatsSummary = memo(function StatsSummary() { /* ... */ });
function Dashboard() {
const [filter, setFilter] = useState('all');
const handleChange = useCallback((value) => setFilter(value), []);
return (
<>
<FilterBar onChange={handleChange} /> {/* stable reference → memo works */}
<StatsSummary /> {/* no props → memo always skips it */}
</>
);
}Try the exercise below. The render count on each component tells you exactly what's happening.
Practice what you just read.
Keep reading