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またはbCheckが変わったかどうかを確認します。変わっていなければ、関数を再度呼び出さずにキャッシュされた結果を返します。
useCallback(fn, [deps])はuseMemo(() => fn, [deps])と同等です。戻り値をキャッシュする代わりに、関数オブジェクト自体をキャッシュします。JavaScriptはレンダリングのたびに新しい関数オブジェクトを作成するため、これは重要です — 子コンポーネントのpropsの参照等値性を壊します。
Reactは各依存関係の値をObject.is()(厳密等値)を使用して比較します。プリミティブ値(数値、文字列、真偽値)は値で比較されます。オブジェクトと配列は参照で比較されます — 依存関係に新しいオブジェクトリテラル'{''}'は常に再計算をトリガーします。
すべての依存関係が前のレンダリングの依存関係と等しい場合、Reactはファクトリ関数を呼び出さずにメモ化された値をすぐに返します。コンポーネントは依然として再レンダリングされますが、高コストな計算はスキップされます。
いずれかの依存関係が変わった場合、Reactはファクトリ関数を再度呼び出し、新しい結果を格納して返します。古いキャッシュ値は破棄されます。
計算の戻り値をキャッシュします。依存関係が変わったときのみ再計算します。props・stateから導出される高コストな計算に役立ちます。
関数参照をキャッシュします。useMemo(() => fn, deps)と同等。メモ化された子コンポーネントにコールバックを渡す際に重要です。
2つのオブジェクト・配列が同じメモリアドレスを指している場合のみ等しいとみなされます。'{''}' === '{''}'はfalse。useMemo・useCallbackはレンダリングをまたいでIDを保持します。
useMemo・useCallbackの第2引数。Reactはこれらの値をレンダリング間で比較して再計算するかどうかを決定します。
コンポーネントのレンダリング出力をメモ化する高階コンポーネント。コールバックpropsを安定させるためにuseCallbackと組み合わせると最も効果的です。
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.