内部理解 — パフォーマンス
React.memo / useMemo / useCallback の本質
「とりあえずuseMemoを使えばいい」は間違い。メモ化の仕組みを理解することで、いつ・なぜ使うべきかがわかります。
🎯 3つの違いを整理する
React.memo コンポーネントのメモ化
propsが変わっていなければ、子コンポーネントの再レンダリングをスキップする。
useMemo 計算結果のメモ化
依存配列が変わっていなければ、前回の計算結果を再利用する。
useCallback 関数のメモ化
依存配列が変わっていなければ、前回と同じ関数参照を返す。
🔑 前提知識:なぜメモ化が必要になるのか
Reactでは、親コンポーネントが再レンダリングされると、すべての子コンポーネントも再レンダリングされます(デフォルト)。 これは子のpropsが変わっていなくても同様です。
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>+</button>
{/* ↓ countが変わるたびに再レンダリングされる */}
<ExpensiveChild data={staticData} />
</div>
);
} ExpensiveChildのprops(staticData)は変わっていないのに、
毎回再レンダリングされます。これを防ぐのがReact.memoです。
⚛️ React.memo:コンポーネントのメモ化
// React.memoでラップ
const ExpensiveChild = React.memo(function({ data }) {
// dataが変わった時だけ再実行される
return <div>{/* 重い処理 */}</div>;
}); ⚠️ 落とし穴:関数をpropsに渡す場合
function Parent() {
const [count, setCount] = useState(0);
// 毎回新しい関数が作られる!
const handleClick = () => console.log('clicked');
return <MemoizedChild onClick={handleClick} />;
// ↑ handleClickの参照が毎回変わる → Memoの意味なし
}
これを解決するためにuseCallbackが必要になります。
🔧 useCallback:関数参照を安定させる
function Parent() {
const [count, setCount] = useState(0);
// ✅ 依存配列が変わらない限り、同じ関数参照を返す
const handleClick = useCallback(() => {
console.log('clicked');
}, []); // 依存なし → 毎回同じ参照
return <MemoizedChild onClick={handleClick} />;
// ✅ handleClickの参照が変わらない → Memoが有効
} useCallback は useMemo の関数版
// この2つは等価 useCallback(fn, deps); useMemo(() => fn, deps);
💡 useMemo:重い計算をキャッシュする
function Component({ items, filter }) {
// ❌ 毎回のレンダリングで重い計算が走る
// const filtered = expensiveFilter(items, filter);
// ✅ filterまたはitemsが変わった時だけ再計算
const filtered = useMemo(
() => expensiveFilter(items, filter),
[items, filter]
);
return <List items={filtered} />;
} useMemoを使うべきタイミング
- 計算コストが高い処理(フィルタ・ソート・集計など)
- 下流のReact.memoコンポーネントに渡すオブジェクト・配列(参照の安定化)
- useEffectの依存配列に入れるオブジェクト
⚡ メモ化はただじゃない
メモ化にはコストがあります。「すべてにuseMemoを使えばいい」は間違いです。
メモ化のコスト
メモリ 前回の値・関数・計算結果をメモリに保持し続ける
比較コスト 毎回依存配列を Object.is で比較するオーバーヘッド
複雑さ コードの可読性が下がり、バグを生みやすくなる
使うべきでない場合
- 計算が単純(数値の足し算など)な場合
- コンポーネントが頻繁に再レンダリングされない場合
- 依存配列の値が毎回変わる場合(メモ化の恩恵なし)
📌 まとめ
- ✓ React.memo:propsが変わらなければ子コンポーネントの再レンダリングをスキップ
- ✓ useMemo:重い計算結果をキャッシュ(依存配列が変わった時だけ再計算)
- ✓ useCallback:関数参照を安定させる(React.memoと組み合わせて使う)
- ✓ 関数・オブジェクトは毎回新しい参照が生まれるため、参照の安定化が重要
- ✓ メモ化には比較コスト・メモリコストがある——必要な場所だけ使う