React useMemoで重い計算結果を再利用する
一覧の絞り込みや並べ替えを描画のたびに行うと、データ量や計算内容によっては画面操作が遅くなります。useMemoは、依存する値が変わらない間、以前の計算結果を再利用します。
絞り込み結果を計算する
import { useMemo, useState } from 'react';
type Product = { id: string; name: string; price: number };
export const ProductList = ({ products }: { products: Product[] }) => {
const [query, setQuery] = useState('');
const filteredProducts = useMemo(() => {
const normalized = query.trim().toLowerCase();
return products.filter((product) =>
product.name.toLowerCase().includes(normalized),
);
}, [products, query]);
return (
<>
<input value={query} onChange={(event) => setQuery(event.target.value)} />
<ul>
{filteredProducts.map((product) => (
<li key={product.id}>{product.name}:{product.price}円</li>
))}
</ul>
</>
);
};productsまたはqueryが変わると再計算します。それ以外の理由で再描画されても、以前の結果を使います。
計算結果をstateへ保存しない
filteredProductsはpropsとstateから計算できる値です。別のstateにしてEffectで同期すると、更新漏れや一時的な不一致が起こりやすくなります。まずは描画中に計算し、実際に計算コストが問題になる場合にuseMemoを検討します。
軽い計算には使わない
const fullName = `${firstName} ${lastName}`;この程度の計算にuseMemoを加えると、依存配列の管理が増えるだけの場合があります。useMemoは正しさのためではなく性能上の最適化です。外しても結果が変わらないコードにします。
React DevToolsのProfilerなどで時間がかかる箇所を測ってから導入すると、不要な最適化を避けられます。
計算内で使用するpropsやstateを依存配列から外すと、古い結果を使い続ける可能性があります。