再レンダリングの最適化に使う「メモ化」系の機能について、自分用に整理。
言葉だけだと混乱しがちなので、それぞれの目的・使いどころ・注意点も一緒に記載
🔁 そもそも「メモ化」とは?
同じ入力に対して同じ出力が返る処理において、その結果をキャッシュして使い回すこと。
Reactでは、再レンダリングのたびに同じ関数や計算結果を無駄に再生成しないために使う。
useCallback:関数のメモ化
const memoizedFn = useCallback(() => {
// 関数の処理
}, [依存値])
目的
- 関数の再定義を防ぐ
- 特に props 経由で子コンポーネントに渡すときに有効
よくあるユースケース
- イベントハンドラ(onClick, onChange など)
- useEffect の依存配列に入れる関数
注意点
- useCallback(fn, []) は「完全に固定した関数」という意味
- 依存配列が間違っているとバグの温床になる
useMemo:値のメモ化
const memoizedValue = useMemo(() => {
return 高コストな計算()
}, [依存値])
目的
- 計算結果をキャッシュする
- 再レンダリングのたびに重い計算を避ける
よくあるユースケース
- .filter(), .map() などの配列処理
- 条件分岐の多いオブジェクト生成
注意点
- useMemo でのパフォーマンス改善は 「重い処理」が前提
- 軽い処理に使っても逆にオーバーヘッドになる可能性がある
React.memo:コンポーネントのメモ化
const SomeComponent = React.memo((props) => {
return <div>{props.text}</div>
})
目的
- 親コンポーネントが再レンダリングされても、子が再描画されないようにする
よくあるユースケース
- props に変更がない限り再描画したくない一覧アイテム
- 重い描画処理を持つコンポーネント
注意点
- props の比較はデフォルトで「浅い比較(shallow compare)」
- 配列やオブジェクトがpropsにある場合 → areEqual 関数を使って明示的に比較する必要あり
補足:再レンダリングの最適化ポイント
- useCallback / useMemo だけで完全にパフォーマンスが良くなるとは限らない
- あくまで「不要な再レンダリングを防ぐ手段のひとつ」
- 状態管理や Redux, Zustand, Context の使い方も併せて最適化するべき
React.memo がデフォルトで効いていない理由
「なぜ React は最初から全コンポーネントに React.memo を適用しないのか?」
- 比較処理(props の shallow compare)にコストがかかる
- React.memo は props の変更を比較して「再レンダリングすべきかどうか?」を判断する
- この「比較」は shallow(浅い)とはいえ、オブジェクトや配列などがあると毎回それを走査することになる
- コンポーネント数が多かったり、props が複雑だったりすると逆に処理が重くなる
- 結果、描画回数が減っても比較のコストが上回ればパフォーマンスが悪化するケースもある
- 純粋な関数コンポーネント(=副作用がない)なら再レンダリングしても問題ないことが多い
- 状態や副作用がない「プレゼンテーショナルコンポーネント」は軽い処理が多く、再レンダリングしても負荷は大きくない
- なのでReact は「不要な最適化はしない。必要になったら自分でやってね」という方針を取っている
結論
- useCallback:関数をメモ化 → 子への props 最適化
- useMemo:値をメモ化 → 再計算防止
- React.memo:コンポーネント自体をメモ化 → 再レンダリング防止