memo
リファレンス
memo(Component, arePropsEqual?)
コンポーネントを memo
でラップすることで、そのコンポーネントのメモ化 (memoize) されたバージョンが得られます。このメモ化されたバージョンのコンポーネントは、親コンポーネントが再レンダーされても、自身の props が変更されていない限り通常は再レンダーされなくなります。ただしメモ化は、パフォーマンス最適化であって保証ではないため、React が再レンダーを行うこともありえます。
引数
Component
: メモ化したいコンポーネント。memo
はこのコンポーネントを変更するのではなく、メモ化が有効になった新たなコンポーネントを返します。関数コンポーネントや によるコンポーネントを含む、任意の有効な React コンポーネントを受け付けます。省略可能
arePropsEqual
: コンポーネントの前回の props と新しい props の 2 つを引数に取る関数。古い props と新しい props が等しい場合、つまり、新しい props でもコンポーネントが古い props と同じ出力をレンダーして同じように動作する場合はtrue
を返すようにします。それ以外の場合はfalse
を返すようにします。通常はこの関数を指定することはありません。デフォルトでは、React は個々の props を を使って比較します。
返り値
memo
は新しい React コンポーネントを返します。これは memo
に渡したコンポーネントと同様に動作しますが、親が再レンダーされた際に自身の props が変更されていない場合、React が再レンダーを行わない、という点が異なります。
使用法
props が変更されていない場合に再レンダーをスキップする
React は通常、親コンポーネントが再レンダーされると常にコンポーネントを再レンダーします。memo
を使用すると、新しい props が古い props と同じである限り、親が再レンダーされても React によって再レンダーされないコンポーネントを作成できます。そのようなコンポーネントはメモ化された (memoized) コンポーネントと呼ばれます。
コンポーネントをメモ化するには、それを memo
でラップし、返された値を元のコンポーネントの代わりに使用します。
React コンポーネントは常にを持つべきです。これは、props、state およびコンテクストが変更されていない場合、常に同じ出力を返す必要があるという意味です。memo
を使用することで、コンポーネントがこの要件を満たしており、props が変更されない限り再レンダーの必要がないということを React に伝えることができます。memo
を使用しても、コンポーネント自体の state が変更された場合や、使用しているコンテクストが変更された場合には再レンダーが発生します。
以下の例において、name
は(props の一部なので)変更されるたびに Greeting
コンポーネントが再レンダーされる一方で、address
は(Greeting
に props として渡されていないため)変更されても再レンダーされないということに注目してください。
さらに深く知る
あらゆる場所に memo を追加すべきか?
あらゆる場所に memo を追加すべきか?
あなたのアプリがこのサイトのように、ほとんどのインタラクションが大まかなもの(ページ全体やセクション全体の置き換えなど)である場合、メモ化は通常不要です。一方、あなたのアプリが描画エディタのようなもので、ほとんどのインタラクションが細かなもの(図形を移動させるなど)である場合、メモ化は非常に役に立つでしょう。
memo
による最適化は、コンポーネントが全く同一の props で頻繁に再レンダーされ、しかもその再レンダーロジックが高コストである場合にのみ価値があります。コンポーネントが再レンダーされても遅延を感じられない場合、memo
は不要です。レンダー中に定義されたオブジェクトやプレーンな関数を渡しているなどでコンポーネントに渡される props が毎回異なる場合、memo
は全く無意味であることを覚えておいてください。これが、memo
と一緒に や がよく必要となる理由です。
その他のケースでコンポーネントを memo
でラップすることにメリットはありません。それを行っても重大な害はないため、個別のケースを考えずに、可能な限りすべてをメモ化するようにしているチームもあります。このアプローチのデメリットは、コードが読みにくくなることです。また、すべてのメモ化が効果的なわけではありません。例えば、毎回変化する値が 1 つ存在するだけで、コンポーネント全体のメモ化が無意味になってしまうこともあります。
実際には、以下のいくつかの原則に従うことで、多くのメモ化を不要にすることができます。
- コンポーネントが他のコンポーネントを視覚的にラップするときは、それが。これにより、ラッパコンポーネントが自身の state を更新しても、React はその子を再レンダーする必要がないことを認識します。
- ローカル state を優先し、必要以上に を行わないようにします。フォームや、アイテムがホバーされているかどうか、といった頻繁に変化する state は、ツリーのトップやグローバルの状態ライブラリに保持しないでください。
- 保ちます。コンポーネントの再レンダーが問題を引き起こしたり、何らかの目に見える視覚的な結果を生じたりする場合、それはあなたのコンポーネントのバグです! メモ化を追加するのではなく、バグを修正します。
- 。React アプリケーションのパフォーマンス問題の大部分は、エフェクト内での連鎖的な state 更新によってコンポーネントのレンダーが何度も引き起こされるために生じます。
- 。例えば、メモ化する代わりに、オブジェクトや関数をエフェクトの中や外に移動させるだけで、簡単に解決できる場合があります。
それでも特定のインタラクションが遅いと感じる場合は、、どのコンポーネントでのメモ化が最も有効かを確認し、そこでメモ化を行いましょう。これらの原則を守ることで、コンポーネントのデバッグや理解が容易になるため、常に原則に従うことをおすすめします。長期的には、この問題を一挙に解決できるについて研究を行っています。
state を使ってメモ化されたコンポーネントを更新する
コンポーネントがメモ化されていても、自身の state が変更されたときには再レンダーが発生します。メモ化は、親からコンポーネントに渡される props にのみ関係します。
state 変数を現在値そのものにセットする場合、React は memo
がなくてもコンポーネントの再レンダーをスキップします。コンポーネント関数が余分に呼び出されることがあるかもしれませんが、その結果は破棄されます。
コンテクストを使ってメモ化されたコンポーネントを更新する
コンポーネントがメモ化されていても、使用しているコンテクストが変更されたときには再レンダーが発生します。メモ化は、親からコンポーネントに渡される props にのみ関係します。
コンテクストの一部が変化したときだけコンポーネントが再レンダーされるようにするには、コンポーネントを 2 つに分割してください。外側のコンポーネントでコンテクストから必要な情報を読み取って、それをメモ化された子コンポーネントに props として渡します。
props の変更を可能な限り減らす
memo
を使用すると、コンポーネントは props のいずれかが浅い (shallow) 比較で前回と等しくない場合に再レンダーされます。つまり、React はコンポーネントのすべての props を前回の値と を使用して比較します。Object.is(3, 3)
は true
ですが、Object.is({}, {})
は false
です。
memo
の利点を最大限活かすためには、props が変更される回数を最小限に抑えます。例えば、ある props がオブジェクトである場合、親コンポーネント側で、そのオブジェクトが毎回再作成されるのを防ぐために を使用します。
props の変更を最小限に抑えるより良い方法は、コンポーネントが最小限の情報を props として受け入れるようにすることです。例えば、オブジェクト全体の代わりにその中の個々の値を受け入れるようにできます。
さらにそのような個々の値を、より変化しづらい値に投射できることもあります。例えば以下では、コンポーネントが値そのものではなく、値が存在するかどうかのみを表すブーリアンを受け入れるようになっています。
メモ化されたコンポーネントに関数を渡す必要がある場合、それが変化しないようにコンポーネント外で宣言するか、または を使用することで再レンダーをまたいで定義をキャッシュします。
カスタム比較関数の指定
まれに、メモ化されたコンポーネントの props の変更を最小限に抑えることが不可能な場合があります。その場合、カスタム比較関数を提供することができます。React は浅い比較の代わりに、これを使用して古い props と新しい props を比較します。この関数は memo
の第 2 引数として渡します。新しい props が古い props と同じ出力をもたらす場合にのみ true
を返し、それ以外の場合は false
を返すようにします。
これを行う際は、ブラウザの開発者ツールの Performance パネルを使用して、比較関数を用いることでコンポーネントを再レンダーするより実際に高速化されることを確認してください。結果に驚くかもしれません。
パフォーマンス測定を行うときは、React が本番モードで動作していることを確認してください。
トラブルシューティング
props がオブジェクト・配列・関数の場合にコンポーネントが再レンダーされる
React は古い props と新しい props とを浅く比較します。つまり、新しい props のそれぞれの値が古い props と参照ベースで等価であるかどうかを比較します。親が再レンダーのたびに新しいオブジェクトや配列を作成している場合、個々の要素が同じであっても、React は変更があったと考えます。同様に、親コンポーネントのレンダー時に新しい関数を作成すると、関数の定義が同じであっても、React はそれを別のものだと考えます。これを避けるためには、。