React.memoの「正体」と、賢いエンジニアが知っておくべき最適化の勘所
現場でコードをレビューしていると、よく「とりあえず全部のコンポーネントを`React.memo`で囲っておけば速くなるだろう」という誤解に遭遇します。結論から言えば、それは「重い処理を避けるために、無駄な計算コストを払う」という本末転倒な状況を生みかねません。
今日は、Reactのパフォーマンス最適化の代名詞とも言える`React.memo`について、その「中身」と「現場での正しい付き合い方」を深掘りしていきましょう。
—
1. なぜReact.memoは「浅い比較(Shallow Compare)」をするのか
まず、裏側の話をします。Reactが親コンポーネントのレンダリング時に子コンポーネントを再評価するかどうかを決める際、`React.memo`はpropsに対して`Object.is`を用いた浅い比較を行います。
これは、「オブジェクトの中身まで再帰的にチェックするのはコストが見合わない」というReactチームの合理的な判断です。もし深い階層まで比較(Deep Compare)しようとすれば、それはレンダリングの節約分を遥かに上回るCPU負荷を生むことになります。
エンジニアとしての心得:
`React.memo`は魔法ではありません。propsに毎回新しい参照(リテラルや関数など)を渡してしまえば、結局「差分あり」と判定され、メモ化の意味は消滅します。
—
2. 実践:比較関数をカスタマイズする(areEqual)
デフォルトの比較では物足りない、あるいはpropsが複雑なケースでは、第二引数に「比較関数(areEqual)」を渡すことで、再レンダリングの条件を細かく制御できます。
import React, { memo } from ‘react’;
// プロパティの型定義
interface Props {
user: { id: number; name: string };
onUpdate: () => void;
}
// 比較関数:特定の条件だけ再レンダリングを許可する
const areEqual = (prevProps: Props, nextProps: Props) => {
// ユーザーIDが変わっていないなら、レンダリングをスキップする
// これにより、Reactが標準で行うprops全体の浅い比較をオーバーライドできる
return prevProps.user.id === nextProps.user.id;
};
const UserProfile = memo(({ user, onUpdate }: Props) => {
console.log(‘UserProfileがレンダリングされました’);
return
;
}, areEqual);
現場の知見:
このカスタム比較関数を使うのは、「どうしても再レンダリングを抑えたい特定の重いコンポーネント」がある時だけにしてください。多用すると、コードの可読性が下がる上に、比較関数のロジック自体がバグの温床になります。
—
3. 「メモ化の罠」を回避するベストプラクティス
`React.memo`を活かすには、受け取る側のコンポーネントだけでなく、「渡す側の親」のケアが不可欠です。
よくあるアンチパターン
// 親コンポーネント
const Parent = () => {
// 毎回新しい関数インスタンスが生成されるため、
// 子がmemo化されていても無意味になる
const handleClick = () => console.log(‘clicked’);
return
}
正解のパターン
`useCallback`で関数を固定し、`useMemo`でオブジェクトの参照を保持することで、初めて`React.memo`は真価を発揮します。
import React, { useCallback, useMemo } from ‘react’;
const Parent = () => {
// 関数をメモ化して参照を固定
const handleClick = useCallback(() => {
console.log(‘clicked’);
}, []);
// オブジェクトをメモ化して参照を固定
const config = useMemo(() => ({ theme: ‘dark’ }), []);
return
}
—
4. シニアアーキテクトからの助言
最後に、一番大切なことを伝えます。「最適化は計測から始めろ」ということです。
React DevToolsの「Profiler」を使い、実際に「レンダリングが原因でボトルネックになっている箇所」を特定してください。なんとなく不安だからと`memo`を乱用すると、コンポーネント間の依存関係が複雑になり、後の改修で「なぜかUIが更新されない」というデバッグ地獄に陥るリスクがあります。
- まずはコンポーネントをシンプルに保つ
- 次に不要なレンダリングをProfilerで特定する
- 最後に、`React.memo`を必要最小限の箇所に適用する
この順序を徹底するだけで、あなたの書くReactコードの品質は劇的に向上します。技術は手段であり、目的は「ユーザーにとって快適な体験」と「チームにとって保守しやすいコード」の維持であることを忘れないでください。
何か迷ったときは、公式ドキュメントの「Escape Hatches」を読み直すのが一番の近道です。現場からは以上です。

コメント