概要
- Reactアプリケーションで、高コストな計算結果のメモ化やレンダー間での計算結果保持を実現できる
useMemo
フックを紹介 - レンダリングごとに再計算される処理の最適化に有効
- 文字列加工、配列やオブジェクトのフィルタリング・ソート計算、複雑な数値計算などで頻出
useMemo
によって 「不要な再計算を避けてパフォーマンスを向上させる」 UIを作成できる
実施条件
- React + TypeScript プロジェクトが構築済みであること
- React Hooksの基本(
useState
,useEffect
など)を理解していること
環境
ツール | バージョン | 目的 |
---|---|---|
Node.js | 22.5.1 | Reactアプリ実行環境 |
React | 19.1.0 | UI構築 |
TypeScript | 4.9 | 型定義による安全な開発 |
useMemo<T>(計算関数, 依存配列)
の役割
-
useMemo
のジェネリクス<T>
は、返り値がどんな型かをTypeScriptに伝える - 依存配列が変化したときのみ計算を実行し、変化がない場合は前回の値を再利用
useMemo
の基本構造
- importセクション
- 型定義セクション
- 関数定義セクション
3.1 内部状態管理セクション
3.2 イベントハンドラー処理セクション
3.3 副作用処理セクション
3.4 返り値構築・ロジックセクション
基本構文
// 1. importセクション
import React, { useState, useMemo } from 'react';
// 2. 型定義セクション
// 今回は不要(返り値型は自動推論される)
// 3. 関数定義セクション
const ExpensiveCalculation = () => {
// 3.1 内部状態管理セクション
const [count, setCount] = useState(0);
// 3.2 イベントハンドラー処理セクション
const handleIncrement = () => setCount((c) => c + 1);
// 3.3 副作用処理セクション
// 今回は副作用なし(計算は useMemo により最適化)
// 3.4 返り値構築・ロジックセクション
const double = useMemo(() => {
console.log('計算実行');
return count * 2;
}, [count]);
return (
<div style={{ padding: '1rem' }}>
<p>現在の値: {count}</p>
<p>計算結果(2倍): {double}</p>
<button onClick={handleIncrement}>+1</button>
</div>
);
};
export default ExpensiveCalculation;
活用例
1. 文字列の長さ制限(タイトル省略)
// 1. importセクション
import React, { useMemo } from 'react';
// 2. 型定義セクション
interface Props {
title: string;
maxTitleLength: number;
}
// 3. 関数定義セクション
const TruncatedTitle: React.FC<Props> = ({ title, maxTitleLength }) => {
// 3.1 内部状態管理セクション
// 今回は特になし(props利用)
// 3.2 イベントハンドラー処理セクション
// 今回は特になし
// 3.3 副作用処理セクション
// 今回は特になし
// 3.4 返り値構築・ロジックセクション
const truncatedTitle = useMemo(() => {
if (title.length <= maxTitleLength) {
return title;
}
const words = title.split(' ');
let result = '';
for (const word of words) {
const testResult = result ? `${result} ${word}` : word;
if (testResult.length <= maxTitleLength - 1) {
result = testResult;
} else {
break;
}
}
if (!result) {
result = title.substring(0, maxTitleLength - 1);
}
return result + '…';
}, [title, maxTitleLength]);
return <p>{truncatedTitle}</p>;
};
export default TruncatedTitle;
2. 配列のフィルタリング(検索ワード)
import React, { useState, useMemo } from 'react';
const FilterList = () => {
// 3.1 内部状態管理セクション
const [search, setSearch] = useState('');
const items = ['apple', 'banana', 'orange', 'grape', 'melon'];
// 3.2 イベントハンドラー処理セクション
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => setSearch(e.target.value);
// 3.3 副作用処理セクション
// 今回はなし
// 3.4 返り値構築・ロジックセクション
const filteredItems = useMemo(() => {
console.log('フィルタリング実行');
return items.filter((item) => item.includes(search));
}, [search]);
return (
<div style={{ padding: '1rem' }}>
<input type="text" value={search} onChange={handleChange} placeholder="検索ワード" />
<ul>
{filteredItems.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
};
export default FilterList;
3. 高コスト計算:配列の合計値
import React, { useState, useMemo } from 'react';
const SumCalculator = () => {
// 3.1 内部状態管理セクション
const [numbers, setNumbers] = useState([1, 2, 3, 4, 5]);
// 3.2 イベントハンドラー処理セクション
const handleAddNumber = () =>
setNumbers([...numbers, numbers.length + 1]);
// 3.3 副作用処理セクション
// 今回はなし
// 3.4 返り値構築・ロジックセクション
const sum = useMemo(() => {
console.log('合計計算実行');
return numbers.reduce((acc, n) => acc + n, 0);
}, [numbers]);
return (
<div style={{ padding: '1rem' }}>
<p>配列: {numbers.join(', ')}</p>
<p>合計: {sum}</p>
<button onClick={handleAddNumber}>数字追加</button>
</div>
);
};
export default SumCalculator;
useMemo
の用途まとめ
ユースケース | 解説 |
---|---|
高コスト計算の最適化 | レンダリングごとに再計算される処理を制御 |
配列やオブジェクト操作 | フィルタリング、ソート、集計などの計算結果保持 |
文字列加工 | 長さ制限、整形、変換処理を効率化 |
レンダリング最適化 | 再レンダリング時に無駄な計算を避けてパフォーマンス向上 |
useRef
, useMemo
, useCallback
の違い
フック | 主な役割 | 値の更新タイミング | レンダーへの影響 | ユースケース |
---|---|---|---|---|
useRef | レンダーに依存しない値の保持 |
.current に代入したとき |
しない(再レンダーをトリガーしない) | - DOM要素へのアクセス(例: inputRef.current.focus() )- タイマーID管理 - 前回値の保持 - 外部ライブラリ連携 |
useMemo | 値のメモ化(計算結果キャッシュ) | 依存配列の値が変わったとき | する(依存値が変われば計算してレンダーに反映) | - 計算コストの高い処理の最適化 - 文字数制限処理 - リストのフィルタリングなどUIに使う計算 |
useCallback | 関数のメモ化 | 依存配列の値が変わったと | しない(関数参照が安定するだけ) | - 子コンポーネントにコールバックを渡す - 再レンダー最適化 |