跪拜 Guibai
← All articles
React.js · Frontend Framework

React's Render Cascade and the One Hook That Stops It

By 小月土星 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Unnecessary re-renders are the most common React performance tax, and the memo/useCallback pairing is the standard prescription. Misapplying either tool—or skipping useCallback when callbacks hit memo boundaries—leaves optimization on the table and trains developers to ignore real render waste.

Summary

React's default behavior re-renders every child when a parent updates, a conservative strategy that often wastes cycles. While React.memo can block re-renders by shallow-comparing primitive props, it fails when a function prop gets a new reference on every render. useCallback caches a function instance and only replaces it when declared dependencies change, closing the reference-drift loophole.

The fix is a two-part mechanism: memo decides whether a child should render, and useCallback ensures the props memo inspects remain referentially stable. Without both, passing callbacks to memo-wrapped components provides no optimization benefit.

A guiding principle emerges: apply useCallback only when a function crosses a memo boundary. Native DOM handlers don't need it, and premature wrapping adds complexity without measurable gain.

Takeaways
React re-renders all children by default when a parent updates, regardless of whether their props changed.
React.memo blocks re-renders via shallow prop comparison but fails on function props because a new reference is created every render.
useCallback returns the same function reference across renders unless its dependency array values change.
Pairing useCallback with React.memo stabilizes callback props so shallow comparison succeeds and child renders are skipped.
useCallback is syntactic sugar for useMemo: useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).
Only apply useCallback when a function is passed to a component wrapped in React.memo; native DOM elements don't benefit.
Before adding useCallback, ask whether the unoptimized path causes a user-visible slowdown—if not, skip it.
Conclusions

The article frames React performance work as preventing renders, not accelerating them, which shifts the optimization mindset from micro-benchmarks to structural waste elimination.

Treating useCallback and React.memo as a 'symbiotic pair' clarifies why developers who adopt memo without useCallback see no gains and then dismiss both tools.

The 'soul-searching question'—does this actually cause lag?—is a practical litmus test that many performance guides omit, yet it prevents the most common over-optimization mistake in React codebases.

Concepts & terms
React.memo
A higher-order component that performs a shallow comparison of props and skips re-rendering the wrapped component if no prop values have changed by reference.
useCallback
A React Hook that returns a memoized version of a callback function, preserving its reference identity between renders unless values in its dependency array change.
Shallow comparison
An equality check that compares only the top-level values of two objects or arrays by reference, not by deep structural equality. React.memo uses this to decide whether props have changed.
Reference drift
The phenomenon where a new function or object reference is created on every render, causing shallow-comparison optimizations like React.memo to fail even when the underlying data is logically unchanged.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗