Master React.memo, useCallback & useMemo to Supercharge Component Performance
This article explains how React.memo, its second‑argument comparator, and the related hooks useCallback and useMemo can be used to avoid unnecessary re‑renders, handle function props correctly, and dramatically improve rendering performance in complex functional components.
React.memo
Before React 16.6, function components lacked a built‑in way to skip re‑execution like
shouldComponentUpdateor
PureComponent. Developers had to rely on conditional rendering or higher‑order components, and function components were compiled to a single
createElementcall.
With the introduction of
React.memoin 16.6, function components gain a
PureComponent-like optimization. The basic usage is:
<code>const C = (props) => {
return <section>{props.name}你太美</section>
}
export default React.memo(C)
</code>When the parent re‑renders,
Cwill shallow‑compare its props; if every prop value is identical, the component’s function is skipped, reducing unnecessary work.
Second Argument of memo
The optional second argument is a comparator function receiving
nextPropsand
prevProps. Returning
truetells React the props are equal, preventing a re‑render; returning
falseforces the component to update.
<code>memo(IfEqual, () => false);
</code>If the comparator always returns
false, the wrapped component will re‑execute on every render.
Function Props in Function Components
Functions are reference types, so even identical inline functions are not
===. Passing an inline
onClickhandler creates a new function each render, causing shallow comparison to fail.
<code>const handleClick = () => {};
export default () => (
<div>
<IfEqual onClick={handleClick} />
</div>
);
</code>To avoid this, move the function definition outside the component or use
memo's second argument to ignore the function prop when appropriate.
useCallback
useCallback(fn, deps)memoizes a function based on its dependency array. When dependencies stay the same, the same function instance is returned, preventing unnecessary re‑creation.
<code>const handleClick = useCallback(() => {
console.log(dep);
}, [dep]);
</code>If
depnever changes,
handleClickremains identical across renders; changing
depproduces a new function.
useMemo
useMemo(() => value, deps)caches the result of an expensive computation. It is equivalent to
useCallback(fn, deps)when the cached value is a function.
<code>const a = useMemo(() => memorizeValue, [deps]);
</code>Example of a heavy computation:
<code>function slowlyAdd(n) {
console.time('add slowly');
let res = n;
for (let i = 0; i < 2000000000; i++) {
res += 1;
}
console.timeEnd('add slowly');
return res;
}
</code>A custom hook can memoize this expensive result:
<code>function useSlowlyAdd(n) {
const res = useMemo(() => slowlyAdd(n), [n]);
return res;
}
</code>Using
useMemoensures the heavy calculation runs only when its dependency
nchanges, keeping the UI responsive.
Higher‑Order Component with useMemo
Even a HOC can be created with
useMemo:
<code>const HOC = useMemo(() => <C />, deps);
</code>Final Thoughts
Wrapping large components (e.g., a 100k‑node
Bigcomponent) with
React.memoand ensuring stable function props with
useCallbackcan dramatically improve mount and update performance, but over‑optimizing components that don’t benefit can actually degrade performance.
When a prop is a function, the only way to keep it stable is to define it outside the component or memoize it with useCallback .
Performance charts (images omitted) illustrate the speed gains when
Bigis memoized versus not memoized.
Tencent IMWeb Frontend Team
IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.