React の memo、useMemo、useCallback をいつ使うべきかを、実際のレンダリング回数を表示しながら解説します。さらに、ref の転送と React 19 でプロップスとして渡せるようになった ref、ポータルを使ったモーダル、一意のIDを使ったラベル、10,000行の仮想リストも紹介します。
コンポーネントを memo で包むと、プロップスが変わらないときにレンダリングを省略できます。関数のプロップスをレンダリング間で同じに保つにはコールバックのメモ化を、オブジェクトや重い計算の結果をキャッシュするには値のメモ化を使います。これらが効果を発揮するのは、memo と組み合わせたときか、実際に処理のコストがかかっているときだけです。まずは React Profiler で計測しましょう。下のライブカウンターで、その違いを実際に確認できます。
それぞれを一言で
デモでは、あなたのブラウザーで実際に発生したレンダリングを数えます。スイッチをオフにした状態とオンにした状態で各ボックスに入力し、カウンターの変化を見てください。後半では、パフォーマンス改善の作業と一緒によく登場するリファレンス的なAPI、つまりref、ポータル、IDについて解説します。
useMemo は渡した関数を呼び出し、その戻り値をキャッシュします。一方、useCallback は関数そのものをキャッシュします。つまり useCallback(fn, deps) は useMemo(() => fn, deps) と同じ意味になります。
いいえ。プロップスの比較にも時間がかかりますし、ほとんどのレンダリングは軽い処理です。プロファイラーで、同じプロップスのまま頻繁にレンダリングされ、しかも目に見える時間がかかっているコンポーネントにだけ使いましょう。
React 19 では関数コンポーネントが ref を通常のプロップスとして受け取れるため、新しいコードではこのラッパーは不要です。今も動作しますが、React チームは将来のリリースで非推奨にすると表明しています。
はみ出しを非表示にしていたり、変形が指定されていたりするカードの中にモーダルを置くと、切り取られたり、他のコンテンツの下に重なったりすることがあります。ポータルを使って文書のボディ直下にレンダリングすれば、Reactのツリーの中に保ったまま、その両方を回避できます。
いいえ。このフックは、ラベルの関連付けや説明の参照といったアクセシビリティ用の属性のためのものです。キーは、データベースのIDなど、データそのものから取得すべきです。
どの問題にどのツールを使うか
ボックスに入力してください。メモ化しないと、プロップスが変わっていなくてもキー入力のたびにリストが再レンダリングされます。メモ化すると、レンダリングは1回だけです。
インラインのアロー関数を渡すと、レンダリングのたびに新しい関数が作られるため、メモ化したコンポーネントの効果がなくなります。useCallback は依存関係が変わるまで同じ関数を返し続けます。
関係のない値が変わっただけなのに、レンダリングのたびに20,000件の商品を絞り込むのは無駄な処理です。値をメモ化すれば、商品か検索語が変わったときだけ絞り込みを再実行するようになります。
関数コンポーネントが転送しない限り、親はそのコンポーネントに ref を渡せません。このラッパーは、受け取った ref を実際のDOM要素まで受け渡します。
React 19 からは、関数コンポーネントが ref を他のプロップスと同じように受け取るため、新しいコードでは転送用のラッパーはもう必要ありません。
ポータルは子要素を別のDOMノード、通常は文書のボディにレンダリングするので、はみ出しが非表示だったり重なり順が低かったりする親から、モーダルを抜け出させることができます。それでも、Reactのイベントは引き続きReactのツリーを通ってバブリングします。
このフックはサーバーとブラウザーのレンダリングで一致する一意のIDを生成するので、同じ入力欄コンポーネントを複数置いても衝突しません。
List は表示範囲内の行と、その前後の数行だけをレンダリングします。各行にはインデックスとスタイルが渡されます。スタイルは行の位置を決めるもので、必ず適用する必要があります。隣のデモは、同じ考え方を手書きで実装したものです。
フォームを作っていますか? useId と相性が良いのは
リストのレンダリング回数:{n}回({count}件)
リストを memo で包む
ここに入力
({n}回レンダリング)
useCallback を使う
小
中
大
選択中:{x}
useMemo でキャッシュ
商品を絞り込む
関係のないクリック({n})
20,000件中{matches}件が一致。絞り込みの実行回数は{runs}回、前回の所要時間は{ms} msでした。
検索
検索欄にフォーカス
このボックスははみ出しを非表示にしていますが、モーダルはウィンドウ全体を覆います。
モーダルを開く
ポータルのデモ
私は createPortal で文書のボディにレンダリングされました。
閉じる
メールアドレス
1日以内に返信します。
メールアドレス(もう一度)
同じコンポーネントで、IDが異なります。
10,000人の顧客
顧客{n}
10,000行ありますが、現在DOMにあるのは{n}行だけです。
無料で始める · クレジットカード不要