現場で差がつく「useMemo」の真実:最適化の罠を回避するプロの視点
フロントエンドの現場にいると、必ず一度はぶち当たる壁がある。「なぜかアプリが重い」。そんなとき、脊髄反射で `useMemo` を全コンポーネントにぶち込むエンジニアをたまに見かける。だが、ちょっと待ってくれ。それは「最適化」ではなく、むしろアプリケーションの首を絞める行為になりかねないんだ。
今日は、Reactの `useMemo` を単なる「おまじない」としてではなく、ブラウザの挙動とReactのレンダリングサイクルを理解した上で、「どこで使い、どこで捨てるべきか」という本質的な話をしよう。
—
1. useMemoの本質:計算コストとのトレードオフ
`useMemo` の役割は、依存配列(Dependency Array)内の値が変化しない限り、前回の計算結果をメモリ上にキャッシュし、再レンダリング時の無駄な計算をスキップすることにある。
だが、ここを勘違いしてはいけない。メモ化そのものに、メモリ消費と「依存配列の比較」というコストが発生する。 つまり、計算コストが極めて低い処理をメモ化するのは、本末転倒だ。逆にメモ化しない方が速い、という事態は往々にして起こる。
どんな時に使うべきか?
- 計算コストが高い処理: 数千件のデータに対する重いフィルタリングやソート。
- 参照の安定性(Referential Equality): 子コンポーネントに渡すオブジェクトや配列の参照を固定し、`React.memo` による不要な再レンダリングを防ぐ場合。
—
2. 実践:高コスト計算をキャッシュする綺麗な書き方
例えば、ユーザーリストから特定の条件でデータを絞り込み、さらに変換処理を行うようなケースだ。これをそのままコンポーネント内に書くと、入力フォームへの入力など、データに関係のない再レンダリングのたびに計算が走ってしまう。
import React, { useMemo, useState } from ‘react’;
const UserList = ({ users, filterKeyword }) => {
// 高コストな計算(フィルタリングと変換)をメモ化
const filteredUsers = useMemo(() => {
console.log(‘重い計算を実行中…’); // デバッグで確認しよう
// 擬似的な高コスト処理(例:複雑なフィルタリング)
return users.filter(user =>
user.name.toLowerCase().includes(filterKeyword.toLowerCase())
).map(user => ({
…user,
displayName: `${user.name} (${user.role.toUpperCase()})`
}));
}, [users, filterKeyword]); // 依存配列:この値が変わった時だけ再計算される
return (
-
{filteredUsers.map(user => (
- {user.displayName}
))}
);
};
ここでのポイント:
依存配列 `[users, filterKeyword]` が変わらない限り、何度コンポーネントが再レンダリングされても `filteredUsers` は以前のキャッシュを返す。Reactは内部で `Object.is` を使って比較している。非常にシンプルだが、強力だ。
—
3. 注意点:初心者がやりがちな「メモ化の罠」
現場でよく見る「アンチパターン」を紹介しておく。これを避けるだけで、君のコードの質は一段上がるはずだ。
- 依存配列を空にする・または適当に書く: 依存配列に書くべき変数を忘れると、永遠に古い結果が表示される(バグの温床)。逆に、不要な変数を書くと、メモ化の意味がなくなる。
- useMemoで副作用を行う: `useMemo` の中で `setState` を呼んだり、APIを叩いたりしてはいけない。計算結果を返すことだけに専念させるのが鉄則だ。副作用は `useEffect` の仕事だ。
- 「とりあえず全部メモ化」: 前述の通り、メモリは有限だ。必要以上にメモ化すると、ブラウザのメモリを圧迫し、ガベージコレクション(GC)の負荷を高める。
—
4. シニアからのアドバイス:計測なき最適化は悪
最後に、一番大切なことを伝える。「直感で最適化するな」ということだ。
React DevToolsの「Profiler」機能を使っているだろうか? レンダリングの回数や所要時間がミリ秒単位で表示されるはずだ。
「なんとなく重い気がする」からメモ化するのではなく、「このコンポーネントの再レンダリングに時間がかかっているから、この計算をメモ化しよう」という根拠を持つこと。これがシニアエンジニアとそうでないエンジニアの決定的な違いだ。
まずは計測して、ボトルネックを見つける。そして `useMemo` を使う。この順番を絶対に守ってほしい。
—
まとめ
- `useMemo` は計算コストが高い処理に絞って使う。
- 子コンポーネントの `memo` 化と組み合わせて、参照の安定化のために使うのも有効。
- 計測(Profiler)なしの最適化は、ただの「推測」であり「技術的負債」になり得る。
Reactは魔法ではない。裏側でどう動いているかを意識すれば、自ずと美しいアーキテクチャが見えてくるはずだ。もしコードを書いていて「これ、本当にメモ化必要か?」と迷ったら、一度外してパフォーマンスを計測してみるといい。その泥臭い検証こそが、君を一段上のエンジニアに引き上げてくれるはずだ。
質問があれば、いつでもコードを持ってきてくれ。一緒にコードレビューしよう。

コメント