Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Join the discussion
Loading comments...
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
React 如何缓存计算值和函数引用以跳过昂贵的重新计算。
记忆化是一种缓存技术,React 存储昂贵计算的结果,并在后续渲染中返回缓存结果 — 当输入未变化时完全跳过计算。useMemo 缓存计算值。useCallback 缓存函数引用。两者都使用依赖数组来决定何时重新计算。
useMemo(() => expensiveComputation(a, b), [a, b]) 在第一次渲染时调用函数并存储结果。在后续渲染时,React 检查 a 或 b 是否变化。如果没有,它返回缓存结果而不再调用函数。
useCallback(fn, [deps]) 等同于 useMemo(() => fn, [deps])。它不是缓存返回值,而是缓存函数对象本身。这很重要,因为 JavaScript 在每次渲染时都会创建新的函数对象 — 这会破坏子组件 props 的引用相等性。
React 使用 Object.is()(严格相等)比较每个 dep 值。基础类型值(数字、字符串、布尔值)按值比较。对象和数组按引用比较 — 因此 deps 中的新对象字面量 '{''}' 总会触发重新计算。
如果所有 deps 都与上次渲染的 deps 相等,React 立即返回记忆化的值而不调用工厂函数。组件仍然重新渲染 — 但昂贵的计算被跳过了。
如果任何 dep 发生变化,React 再次调用工厂函数,存储新结果并返回它。旧的缓存值被丢弃。
缓存计算的返回值。仅在 deps 变化时重新计算。适用于从 props/state 派生的昂贵计算。
缓存函数引用。等同于 useMemo(() => fn, deps)。在向记忆化子组件传递回调时至关重要。
两个对象/数组只有在指向相同内存地址时才相等。'{''}' === '{''}' 为 false。useMemo/useCallback 在渲染之间保持标识。
useMemo/useCallback 的第二个参数。React 在渲染之间比较这些值以决定是否重新计算。
记忆化组件渲染输出的高阶组件。与 useCallback 结合使用效果最佳,以稳定回调 props。
1function ParentComponent({ items }) {2 const [filter, setFilter] = useState('');3 const [count, setCount] = useState(0);45 // useMemo: expensive filter only recomputes when items/filter change6 const filteredItems = useMemo(7 () => items.filter(item => item.name.includes(filter)),8 [items, filter]9 );1011 // useCallback: stable reference, doesn't cause Child re-render12 // when only 'count' changes13 const handleSelect = useCallback((id) => {14 console.log('Selected:', id);15 }, []); // no deps — function never needs to change1617 return (18 <>19 <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>20 <MemoizedChild items={filteredItems} onSelect={handleSelect} />21 </>22 );23}2425const MemoizedChild = React.memo(({ items, onSelect }) => {26 // Only re-renders when items or onSelect reference changes27 return items.map(i => <Item key={i.id} item={i} onSelect={onSelect} />);28});
没有记忆化,每次父组件重新渲染都会创建新的对象/函数引用,导致使用 React.memo 包裹的子组件不必要地重新渲染。useMemo 和 useCallback 保持引用稳定性,使 React.memo 有效,并防止大型组件树中的级联重新渲染。
Your analytics dashboard processes 10,000 rows of sales data to calculate aggregates (sum, average, percentiles) and format display values. A clock that updates every second causes the entire dashboard to re-render — and the 50ms data processing runs every second.
The data transformation runs inside the render function without memoization. Every re-render (even from the clock ticker) recomputes all 10,000 rows. The dashboard becomes sluggish because 50ms of computation blocks the main thread on every tick.
Wrap the data transformation in `useMemo(() => processData(salesData), [salesData])`. Now the heavy computation only re-runs when `salesData` actually changes. Clock ticks cause re-renders but skip the 50ms computation entirely — React reuses the cached result.
Takeaway: useMemo is designed for expensive computations, not every value. The general guideline: if a computation takes more than 1ms or processes more than 100 items, it's a candidate for useMemo. Profile before optimizing — don't guess.
You wrap a heavy `DataTable` component in React.memo, but React DevTools still shows it re-renders every time the parent's unrelated state changes. Memo appears to 'not work.'
The parent passes `onRowClick={(id) => handleClick(id)}` — an inline arrow function. Every parent render creates a NEW function reference. React.memo compares props by reference: `oldFn !== newFn`, so it always re-renders. Memo is working correctly — the props ARE different every time.
Wrap the handler in `useCallback`: `const onRowClick = useCallback((id) => handleClick(id), [handleClick])`. Now the function reference is stable across renders. React.memo's shallow comparison sees identical props and correctly skips re-rendering the DataTable.
Takeaway: React.memo and useCallback/useMemo are a PAIR — they must be used together. React.memo is useless if any prop is an unstable reference (inline function, inline object, inline array). Always stabilize props with useCallback/useMemo before wrapping a child in React.memo.