useMemo 完全入門【React】計算結果をキャッシュしてパフォーマンス最適化
ReactのuseMemoとは何か、計算結果のキャッシュの仕組みと使いどころを初心者向けに解説。useCallbackとの違いや、使いすぎを避けるためのベストプラクティスも紹介します。
Reactアプリのパフォーマンスを改善したいとき、最初に出会うフックの一つが useMemo です。
「重い計算処理が毎回のレンダリングで実行されてしまう」「リストのフィルタリングが遅い」——そんな悩みを解決するのが useMemo です。ただし使いどころを間違えると逆効果になることもあるので、正しく理解しておきましょう。
この記事でわかること:
useMemoが必要な場面と仕組み- 基本的な書き方とパラメータ
useCallbackとの違い- 使いすぎを避けるためのガイドライン
useMemo とは?
useMemo は、計算処理の結果をキャッシュ(メモ化)するフックです。依存配列の値が変わらない限り、前回の計算結果をそのまま使い回します。
const cachedValue = useMemo(calculateValue, dependencies);| パラメータ | 説明 |
|---|---|
calculateValue | キャッシュしたい値を計算する関数(引数なし、純粋関数) |
dependencies | 変化を監視する値の配列 |
動作の仕組み
初回レンダリング:calculateValue を実行 → 結果をキャッシュ
再レンダリング時:
① dependencies が前回と同じ → キャッシュした値をそのまま返す(計算スキップ)
② dependencies が変わった → calculateValue を再実行 → キャッシュを更新
基本的な使い方
重い計算処理のキャッシュ
import { useMemo } from "react";
function ProductList({ products, filterText }) {
// filterText が変わったときだけ再フィルタリング
const filteredProducts = useMemo(
() =>
products.filter((product) =>
product.name.toLowerCase().includes(filterText.toLowerCase())
),
[products, filterText] // この値が変わったら再計算
);
return (
<ul>
{filteredProducts.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}useMemo がなければ、filterText とは無関係な state が変わってもフィルタリング処理が毎回走ってしまいます。
memo コンポーネントとの組み合わせ
import { memo, useMemo, useState } from "react";
// memo でラップした子コンポーネント
const ItemList = memo(function ItemList({ items }) {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
});
function Parent({ data, theme }) {
// theme が変わっても items の参照が変わらない → ItemList の再レンダリングをスキップ
const items = useMemo(
() => data.filter((item) => item.active),
[data]
);
return (
<div className={theme}>
<ItemList items={items} />
</div>
);
}エフェクトの過剰実行を防ぐ
function SearchComponent({ query }) {
// options オブジェクトをメモ化(毎回新しいオブジェクトが作られるのを防ぐ)
const searchOptions = useMemo(
() => ({ minLength: 3, caseSensitive: false }),
[] // 依存なし:一度だけ作成
);
useEffect(() => {
// searchOptions が毎回新しいオブジェクトだとエフェクトが毎回実行されてしまう
searchApi(query, searchOptions);
}, [query, searchOptions]);
}useCallback との違い
よく混同される useCallback との違いを整理します。
| フック | キャッシュするもの | 内部的な実装 |
|---|---|---|
useMemo | 計算結果(値) | useMemo(() => fn, deps) |
useCallback | 関数そのもの | useMemo(() => fn, deps) と同等 |
// useMemo:計算結果をキャッシュ
const filteredList = useMemo(
() => list.filter(isActive),
[list]
);
// useCallback:関数をキャッシュ
const handleClick = useCallback(
() => console.log("クリック"),
[]
);よくあるミス
ミス1:アロー関数の書き方
// ❌ NG:{} はオブジェクトではなくブロックとして解釈される
const data = useMemo(() => { name: "React" }, []);
// → undefined が返される
// ✅ OK:丸括弧でオブジェクトを囲む
const data = useMemo(() => ({ name: "React" }), []);ミス2:依存配列を忘れる
// ❌ NG:依存配列がないので毎回再計算される
const result = useMemo(() => heavyCalculation(data));
// ✅ OK:依存配列を必ず指定する
const result = useMemo(() => heavyCalculation(data), [data]);使いすぎに注意
useMemo をすべての計算に使う必要はありません。
// ❌ 過剰:単純な計算には不要
const doubled = useMemo(() => count * 2, [count]);
// ✅ シンプルに書けばよい
const doubled = count * 2;useMemo 自体にもメモリ使用やオーバーヘッドのコストがあります。使う目安は:
- 処理に明らかに時間がかかる(リストのフィルタリング・ソートなど)
memoでラップした子コンポーネントに渡すオブジェクト・配列useEffectの依存配列に入れるオブジェクト
まず計測して、遅い場合にのみ追加するのがベストプラクティスです。
まとめ
useMemoは計算結果をキャッシュして、不要な再計算を防ぐフックdependenciesが変わらない限り、キャッシュされた値を返し続けるuseCallbackは関数をキャッシュする(useMemoの関数版)- 重い計算・
memoコンポーネントへの props・エフェクトの依存値に使う - パフォーマンス問題が実際に起きてから使うのが正しいアプローチ
PR
useMemo 公式ドキュメントで詳細を確認しよう
useMemoのインタラクティブなサンプルや、React Compilerとの関係など最新情報は公式ドキュメントで確認できます。
useMemo 公式ドキュメントを見る →