Reactの再レンダリングを制御する!memo・useMemo・useCallbackの使い分けを紹介

Reactのパフォーマンス最適化における鍵を握るのが、不要な再レンダリングの抑制です。
アプリケーションが大規模化するにつれ、コンポーネントの描画コストは無視できないものになります。Reactにはこの再レンダリングを制御するための便利なフックやコンポーネントラッパーが用意されており、特に重要なのが React.memo
、useMemo
、useCallback
です。
本記事では、これら3つの違いや適切な使い分け、注意点を初心者の方でも理解できるように、実例を交えながら丁寧に解説いたします。
コンポーネントの再レンダリングとは
Reactでは、状態やプロパティが変更されると該当コンポーネントが再レンダリングされます。
再レンダリングとは、仮想DOMの差分比較(Reconciliation)を行い、実際のDOMに変更を反映する処理です。
この一連の処理は小規模であれば問題になりませんが、以下のようなケースではパフォーマンスが低下します:
- 再レンダリング時に重い処理が走る(例:ソートやフィルター)
- 子コンポーネントが無関係な変更でも再描画される
- useEffectなどの副作用が無駄に実行される
こうした無駄な再レンダリングを防ぐ手段として登場するのが、memo
、useMemo
、useCallback
です。
React.memo:コンポーネント単位のメモ化
最も基本的かつ強力な最適化手法が React.memo
です。
概要
React.memo
は純粋関数コンポーネントをメモ化する高階コンポーネント(HOC)です。
同じ props であれば、再レンダリングをスキップしてくれます。
公式ドキュメント
基本使用例
tsximport React from 'react';
const Button = React.memo(({ onClick, children }: { onClick: () => void; children: React.ReactNode }) => {
console.log('Button rendered');
return <button onClick={onClick}>{children}</button>;
});
解説
この Button
コンポーネントは React.memo
によってラップされているため、props が変わらない限り再レンダリングされません。
以下のような親コンポーネントがあったとします:
tsxexport const Parent = () => {
const [count, setCount] = React.useState(0);
const handleClick = () => {
console.log('Clicked');
};
return (
<>
<p>Count: {count}</p>
<Button onClick={handleClick}>Click me</Button>
<button onClick={() => setCount(count + 1)}>Increment</button>
</>
);
};
このとき、handleClick
が新しい参照になるため、Button
は再レンダリングされます。これを防ぐには後述の useCallback
が必要です。
useCallback:関数の参照を固定する
useCallback
は関数をメモ化し、不必要な再生成を防ぐフックです。
公式ドキュメント
Hooks API Reference - useCallback
使用例
tsxconst handleClick = useCallback(() => {
console.log('Clicked');
}, []);
解説
第二引数の依存配列が変わらない限り、handleClick
は常に同じ関数の参照を返します。
memo
と useCallback
を組み合わせると、無駄な再レンダリングを完全に防ぐことができます。
useMemo:値のメモ化
useMemo
は計算結果をメモ化するためのフックです。
レンダリングごとに実行される処理を避け、前回の値を再利用することでコストを削減します。
公式ドキュメント
基本例
tsxconst sortedList = useMemo(() => {
return list.sort((a, b) => a.localeCompare(b));
}, [list]);
注意点
- 関数の副作用があるものには使わない(useEffectに分離すべき)
- 計算が軽量な場合は不要
使い分けの考え方と整理表
以下のように整理するとわかりやすくなります:
機能 | メモ化対象 | 再評価条件 | 主な用途 |
---|---|---|---|
React.memo | コンポーネント | propsが変わる時 | コンポーネント再レンダリング防止 |
useCallback | 関数 | 依存配列が変わる時 | 関数の再生成防止(memoと併用) |
useMemo | 計算値 | 依存配列が変わる時 | 重い計算結果のキャッシュ |
よくあるパフォーマンス改善パターン
フィルターや検索の処理
tsxconst filteredItems = useMemo(() => {
return items.filter((item) => item.includes(keyword));
}, [items, keyword]);
子コンポーネントのイベントハンドラー
tsxconst handleLike = useCallback(() => {
onLike(itemId);
}, [itemId, onLike]);
過剰な最適化に注意
以下のようなケースでは逆にパフォーマンスが悪化することもあります:
useMemo
による不要なキャッシュuseCallback
による不要な関数ラップReact.memo
による比較コストが高すぎる(deep equalityなど)
「本当にコストが高いか?」を計測した上で適用することが重要です。
開発者ツールでの確認
React DevTools を使えば、コンポーネントの再レンダリング状況を視覚的に確認できます。
コンポーネントを選択し、Render Count
や Props
の変更履歴を確認すると、適切にメモ化されているかが一目で分かります。
総まとめ
本記事では、Reactアプリケーションの再レンダリングを制御するために不可欠な3つの仕組みをご紹介しました。
React.memo
:コンポーネント単位で再レンダリングを防ぐuseCallback
:関数の再生成を防ぐuseMemo
:計算値の再評価を防ぐ
適切な使い分けを実践することで、よりレスポンスの良いUIを構築することが可能になります。
ただし、過剰な最適化は逆効果となる可能性もあるため、「必要な場所だけに使う」意識が大切です。
最後までご覧いただきありがとうございました。
記事Article
もっと見る- article
ReactのuseMemoとは?再レンダリングを防ぐための基本と使い方をわかりやすく解説
- article
開発時にで役立つgit stashコマンドの基本と応用テクニックを紹介
- article
React.memoとは?再レンダリングを防ぐための基本と使い方をわかりやすく解説
- article
Reactの状態管理2025:「useState」「Redux Toolkit」「Jotai」「Zustand」を比較してみた
- article
Next.jsでの画像最適化戦略:next/image vs 外部CDNを比較してみた
- article
React Server Componentsの可能性と課題を実用に向けて考えてみる