React Custom HooksのuseMemoの基本と使い方
Reactアプリケーションが成長するにつれて、パフォーマンスの低下が気になる場面が増えてきます。
とくに、毎回同じ結果しか返さない計算処理や再生成されるオブジェクトや関数が原因で、意図しない再レンダリングが発生してしまうことも少なくありません。
そんな問題を解決するために活躍するのが、Reactの useMemo フックです。
このフックを正しく使いこなすことで、不要な再レンダリングを防ぎ、効率的なコンポーネント構成を実現できます。
本記事では、React初心者の方でもわかりやすいように、基本的な仕組みから応用的な使い方までを丁寧に解説いたします。
公式ドキュメントへのリンクも適宜挿入しながら、手を動かしながら学べる内容となっております。
パフォーマンス最適化に欠かせないuseMemoの基礎知識
useMemo は、計算結果の「メモ化(記憶)」を行うReactのフックです。
コンポーネントの再レンダリング時に、毎回重い計算を行うのではなく、依存する値が変わらない限りは前回の計算結果を再利用します。
基本構文
tsxconst memoizedValue = useMemo(() => {
return heavyCalculation(a, b);
}, [a, b]);
この構文では、aまたはbが変わらない限り、heavyCalculation(a, b)は再評価されません。
公式ドキュメントより
useMemolets you cache the result of a calculation between re-renders.
React: useMemo – Official Docs
実践:useMemoを使ったパフォーマンス改善例
例:重い計算処理のメモ化
tsxconst heavyCalc = (num: number): number => {
console.log('重い計算中...');
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += num * Math.random();
}
return result;
};
const App = () => {
const [count, setCount] = useState(0);
const [other, setOther] = useState(false);
const memoizedResult = useMemo(() => heavyCalc(count), [count]);
return (
<div>
<p>計算結果: {memoizedResult}</p>
<button onClick={() => setCount(count + 1)}>カウント</button>
<button onClick={() => setOther(!other)}>その他</button>
</div>
);
};
このコードでは、「その他」ボタンを押しても heavyCalc は呼ばれません。
countが変わったときのみ再計算されます。
なぜuseMemoが必要なのか?
Reactでは、関数コンポーネントは再レンダリングのたびに関数全体が再実行されるという仕様があります。
そのため、以下のようなケースでは無駄な処理が実行されてしまいます。
- 重い計算処理
- 毎回新しいオブジェクトや配列を生成
- コンポーネント間で同じデータをpropsとして渡す
useMemo を使うことで、これらの処理結果をキャッシュして、無駄なコストを削減できます。
useMemoとReact.memoの違い
| フック | 概要 | 主な使用対象 | メモ化の対象 |
|---|---|---|---|
useMemo | 計算結果をメモ化する | 値(オブジェクト、配列など) | 値(任意の型) |
React.memo | コンポーネント自体のレンダリングをスキップ | コンポーネント | コンポーネント |
これらは併用することで、より強力な最適化が可能になります。
useMemoの使いすぎに注意
useMemo 自体にもオーバーヘッドがあります。
以下のような場面では使用を避けるのが賢明です。
- 処理が軽い計算や単純な値
- 小規模なコンポーネント
- 見た目上影響が少ないUI部品
Profilerなどでボトルネックを確認してから導入するのがベストです。
オブジェクトや配列をpropsとして渡す際のテクニック
オブジェクトや配列は毎回新しいインスタンスとして生成されると、子コンポーネントの React.memo が効果を発揮しません。
悪い例
tsx<Child filters={{ category: 'tech' }} />
これは毎回新しいオブジェクトとして扱われます。
良い例
tsxconst filters = useMemo(() => ({ category: 'tech' }), []);
<Child filters={filters} />
このように useMemo でラップすることで、propsの再評価を防げます。
React.memoとの併用パターン
React.memo と useMemo を組み合わせると、propsの値が変わらない限り、コンポーネントが再描画されない状態を保てます。
tsxconst config = useMemo(() => ({ darkMode: theme === 'dark' }), [theme]);
<Child config={config} />
さらに React.memo の第二引数としてカスタム比較関数を渡せば、より柔軟な最適化が可能です。
useCallbackとの違いと使い分け
| フック | メモ化対象 | 用途 |
|---|---|---|
useMemo | 値(オブジェクトなど) | 計算結果のキャッシュ |
useCallback | 関数 | 関数の再生成防止 |
関数を子に渡す場合は useCallback、値を渡すなら useMemo を選ぶとよいでしょう。
状態管理ライブラリとの連携
ReduxやZustandなどの外部ステート管理ライブラリでも、セレクタの結果をオブジェクトでまとめる場合は useMemo でのメモ化が有効です。
tsxconst config = useMemo(() => ({ darkMode, themeColor }), [darkMode, themeColor]);
Zustandでは shallow 比較を活用することで、再レンダリングを抑える設計も可能です。
ProfilerでuseMemoの効果を確認する
React DevToolsの「Profiler」タブを使えば、どのコンポーネントがいつ再描画されているかを可視化できます。
これにより useMemo の効果が一目でわかり、不要な最適化や足りない工夫に気づけるようになります。
よくある質問
-
Q: useMemoは毎回使うべきですか?
A: いいえ。コストの高い処理にのみ限定するべきです。 -
Q: useMemoの戻り値が関数でもいいですか?
A: はい。ただし関数をメモ化したい場合はuseCallbackの方が適しています。 -
Q: SSRでは有効ですか?
A: SSR中は毎回初期化されるため、クライアントマウント後にのみ有効です。
useMemo活用のまとめ
| チェック項目 | ポイント |
|---|---|
| 計算コストの有無 | 重い処理なら使う |
| オブジェクト・配列の安定性 | useMemoで参照を固定 |
| React.memoやuseCallbackとの併用 | 再レンダリングを防ぐために必須 |
| Profilerによる実測 | オーバーヘッドが逆効果になっていないか確認すること |
おわりに
useMemo は、Reactのパフォーマンス最適化における重要な武器です。
ですが、使いどころを間違えると効果が出ないばかりか、逆に処理の遅延や複雑さを生むこともあります。
本記事を通じて、useMemo の役割と正しい活用法を理解し、Reactアプリの品質向上にお役立ていただければ幸いです。
Reactの内部挙動に目を向け、**「なぜ再描画されるのか?」**を理解しながら、最適な設計を目指してまいりましょう。
articleReact 開発環境の作り方:Vite + TypeScript + ESLint + Prettier 完全セットアップ
articleReact とは? 2025 年版の特徴・強み・実務活用を一気に理解する完全解説
articleESLint を Yarn + TypeScript + React でゼロから構築:Flat Config 完全手順(macOS)
article【徹底比較】Preact vs React 2025:バンドル・FPS・メモリ・DX を総合評価
article既存 React プロジェクトを Preact に移行する完全ロードマップ
articleReact 開発を加速する GitHub Copilot 活用レシピ 20 選
articleZustand の状態管理を使ったカスタムフック作成術
articleJest で Hooks(useState / useEffect)をテストする方法
articleReact × TypeScript:Hooks とコンポーネントの型安全な書き方
articleuseStoreフック徹底解説:Zustandの基本操作をマスターしよう
articleReactのSuspense × useTransitionを使って滑らかなUXを実現する方法
articleReact Custom HooksのuseCallbackの基本と使い方
articleSvelte のコンパイル出力を読み解く:仮想 DOM なしで速い理由
articleTauri で Markdown エディタを作る:ライブプレビュー・拡張プラグイン対応
articleStorybook で“仕様が生きる”開発:ドキュメント駆動 UI の実践ロードマップ
articleshadcn/ui で B2B SaaS ダッシュボードを組む:権限別 UI と監査ログの見せ方
articleSolidJS の Control Flow コンポーネント大全:Show/For/Switch/ErrorBoundary を使い分け
articleRemix で管理画面テンプレ:表・フィルタ・CSV エクスポートの鉄板構成
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来