React.memoとは?
コンポーネントのレンダリング結果をメモ化するReactのAPI、React16.6から導入されました。あくまでpropsの変更に対して再レンダリングの判定が行われるだけでstateやuseContextで保持している値が更新された場合は再レンダリングの対象になるので注意です。
背景
元々、クラスコンポーネントでは「shouldComponentUpdate」や「PureComponent」などによって不要なレンダリングを回避するための機能がありましたが、関数コンポーネントでは必ず再レンダリングされてしまうようになっていました。しかし、このHooksのReact16.6から登場した「React.memo」によって「shouldComponentUpdate」や「PureComponent」と同等のことができるようになりました。
メリット
コンポーネントの再レンダリングを防止し、パフォーマンスを向上させることができる。
用途
下記のようなコンポーネントに対してmemo化すると良い。
親コンポーネントと子コンポーネントがあった場合に、親に子がたくさんぶら下がっている場合で、それぞれ個別に子コンポーネントがstateを保持している場合。
親コンポーネントである子コンポーネントのstateを更新してしまうと他の子コンポーネントにまで影響して再レンダリングされてしまいます。各子コンポーネントをmemo化しておくことで必要なコンポーネントのみが再レンダリングされるようになります。
HooksであるuseCallbackと併用することで、コンポーネントの不要な再描画をスキップすることが可能です。
注意点
描画される都度propsの値が変わるようなコンポーネントでは使わない。
小規模なstateしか持たないコンポーネントの場合は、逆にmemoを使うとライフサイクルが発生するコストがかかるだけでパフォーマンスが落ちます。基本的に、propsの値が変更がないのに描画を何度もされるようなコンポーネントで使うと良いでしょう。(フォームのテキストボックスなど)
propsで関数を渡す場合は、useCallbackを使うこと。
せっかく、React.memoでコンポーネントをメモ化しても、propsで即時関数を渡してしまうと再レンダリングされてしまう仕様のようです。その場合は、関数はuseCallbackでラップして渡すようにしましょう。(なお、クラスコンポーネントshouldComponentUpdateでいうところのbindを使ってい対策していたのを、関数コンポーネント+Hooksでの同等の対策のようです。)
実装設計の方針
基本的にすべてのコンポーネントはmemoで囲ってあげればよいと思います。ただ、memo自体にも多少のコストはかかるので、粒度がめちゃめちゃ小さいようなコンポーネントは使わないという判断も可能という感じです。
構文
1 |
React.memo(コンポーネント,関数); |
実装例
コンポーネントのpropsであるaの前回のpropsと今回のpropsで値を比較して等価であれば再描画しません。
1 2 3 |
const Hello = React.memo( ({a}) => { return <h1>Hello {a}</h1>; } ); |
第二引数
あまり使うケースは多くないですが、基本的にはオブジェクトのpropsに対して指定します。
オブジェクト内の一部プロパティの変更発生の場合でも再レンダリングさせたい場合に使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function areEqual(prevProps, nextProps) { if(prevProps.count !== nextProps.count) { return false; // 再レンダリング } else { return true; // 再レンダリング発生なし } /* false:前と後のpropsが異なる。 true: 前と後のpropsが同じである。 */ } const memoComponent = memo(メモ化するコンポーネント(countというpropsを持つ),areaEqual) |
Propsがなくてもメモ化できるらしい。
別にReact.memoは必ずしもpropsを子に渡している場合に動くわけじゃないらしいです。(もちろん親はpropsを持ってる必要があるかと思いますが。)
https://zenn.dev/irico/articles/acafb8bc7fb7f7
この記事へのコメントはありません。