Reactのmemo、useMemo、useCallback(ref、ポータル、useIdも)

Reactのmemo、useMemo、useCallback、forwardRef、ポータル、useId

React memo・useMemo・useCallbackの違い、forwardRef、ポータル、useId | We.Inc

React の memo、useMemo、useCallback をいつ使うべきかを、実際のレンダリング回数を表示しながら解説します。さらに、ref の転送と React 19 でプロップスとして渡せるようになった ref、ポータルを使ったモーダル、一意のIDを使ったラベル、10,000行の仮想リストも紹介します。

コンポーネントを memo で包むと、プロップスが変わらないときにレンダリングを省略できます。関数のプロップスをレンダリング間で同じに保つにはコールバックのメモ化を、オブジェクトや重い計算の結果をキャッシュするには値のメモ化を使います。これらが効果を発揮するのは、memo と組み合わせたときか、実際に処理のコストがかかっているときだけです。まずは React Profiler で計測しましょう。下のライブカウンターで、その違いを実際に確認できます。

それぞれを一言で

デモでは、あなたのブラウザーで実際に発生したレンダリングを数えます。スイッチをオフにした状態とオンにした状態で各ボックスに入力し、カウンターの変化を見てください。後半では、パフォーマンス改善の作業と一緒によく登場するリファレンス的なAPI、つまりref、ポータル、IDについて解説します。

よくある質問

useMemo と useCallback の違いは何ですか?

useMemo は渡した関数を呼び出し、その戻り値をキャッシュします。一方、useCallback は関数そのものをキャッシュします。つまり useCallback(fn, deps) は useMemo(() => fn, deps) と同じ意味になります。

すべてのコンポーネントをメモ化すべきですか?

いいえ。プロップスの比較にも時間がかかりますし、ほとんどのレンダリングは軽い処理です。プロファイラーで、同じプロップスのまま頻繁にレンダリングされ、しかも目に見える時間がかかっているコンポーネントにだけ使いましょう。

forwardRef は非推奨になりましたか?

React 19 では関数コンポーネントが ref を通常のプロップスとして受け取れるため、新しいコードではこのラッパーは不要です。今も動作しますが、React チームは将来のリリースで非推奨にすると表明しています。

モーダルにポータルを使うのはなぜですか?

はみ出しを非表示にしていたり、変形が指定されていたりするカードの中にモーダルを置くと、切り取られたり、他のコンテンツの下に重なったりすることがあります。ポータルを使って文書のボディ直下にレンダリングすれば、Reactのツリーの中に保ったまま、その両方を回避できます。

リストのキーに useId を使えますか?

いいえ。このフックは、ラベルの関連付けや説明の参照といったアクセシビリティ用の属性のためのものです。キーは、データベースのIDなど、データそのものから取得すべきです。

計測の方法

よくある間違い

判断表:memo、useMemo、useCallback、またはそれ以外

どの問題にどのツールを使うか

レンダリング回数のビフォーアフター

memo:プロップスが等しいときにレンダリングを省略する

ボックスに入力してください。メモ化しないと、プロップスが変わっていなくてもキー入力のたびにリストが再レンダリングされます。メモ化すると、レンダリングは1回だけです。

useCallback:関数を同じに保つ

インラインのアロー関数を渡すと、レンダリングのたびに新しい関数が作られるため、メモ化したコンポーネントの効果がなくなります。useCallback は依存関係が変わるまで同じ関数を返し続けます。

useMemo:重い計算をキャッシュする

関係のない値が変わっただけなのに、レンダリングのたびに20,000件の商品を絞り込むのは無駄な処理です。値をメモ化すれば、商品か検索語が変わったときだけ絞り込みを再実行するようになります。

forwardRef、プロップスとしてのref、ポータル、useId

forwardRef(React 18 以前)

関数コンポーネントが転送しない限り、親はそのコンポーネントに ref を渡せません。このラッパーは、受け取った ref を実際のDOM要素まで受け渡します。

プロップスとしてのref(React 19)

React 19 からは、関数コンポーネントが ref を他のプロップスと同じように受け取るため、新しいコードでは転送用のラッパーはもう必要ありません。

モーダルやツールチップのための createPortal

ポータルは子要素を別のDOMノード、通常は文書のボディにレンダリングするので、はみ出しが非表示だったり重なり順が低かったりする親から、モーダルを抜け出させることができます。それでも、Reactのイベントは引き続きReactのツリーを通ってバブリングします。

ラベルと入力欄のペアのための useId

このフックはサーバーとブラウザーのレンダリングで一致する一意のIDを生成するので、同じ入力欄コンポーネントを複数置いても衝突しません。

長いリスト

react-window:10,000行のリスト

List は表示範囲内の行と、その前後の数行だけをレンダリングします。各行にはインデックスとスタイルが渡されます。スタイルは行の位置を決めるもので、必ず適用する必要があります。隣のデモは、同じ考え方を手書きで実装したものです。

フォームを作っていますか? useId と相性が良いのは

React Hook Form

リストのレンダリング回数:{n}回({count}件)

リストを memo で包む

ここに入力

({n}回レンダリング)

useCallback を使う

小

中

大

選択中:{x}

useMemo でキャッシュ

商品を絞り込む

関係のないクリック({n})

20,000件中{matches}件が一致。絞り込みの実行回数は{runs}回、前回の所要時間は{ms} msでした。

検索

検索欄にフォーカス

このボックスははみ出しを非表示にしていますが、モーダルはウィンドウ全体を覆います。

モーダルを開く

ポータルのデモ

私は createPortal で文書のボディにレンダリングされました。

閉じる

メールアドレス

1日以内に返信します。

メールアドレス(もう一度)

同じコンポーネントで、IDが異なります。

10,000人の顧客

顧客{n}

10,000行ありますが、現在DOMにあるのは{n}行だけです。

無料で始める · クレジットカード不要

関連: ガイド|Webマーケティングと集客を学ぶ

製品

ご利用いただける方

機能

リソース

会社情報

サイトマップを見る