何番煎じになるかはわからないですが、React.PureComponentについてまとめてみました。
前提
shouldComponentUpdate
Reactは仮想DOMフレームワークなので、更新が多くなってもDOM操作が積もることはないのですが、コンポーネントが多くなれば仮想DOMの更新だけでも処理がかさんできます。
そこで、shouldComponentUpdate(newProps, newState)というメソッドを用意して、更新したいときだけtrueを、更新不要な場合にはfalseを返すようにすることで、仮想DOMの操作もカットできるようになります。
PureComponent
そして、shouldComponentUpdateを書く場合、多くは「propsとstateのプロパティをそれぞれ比較」というような形になるかと思います。このような形のshouldComponentUpdateを造り付けたComponentがPureComponentです。
PureComponentでは、propsとstateのプロパティそれぞれを、浅い比較(オブジェクトが同じか)でチェックしていきます。
動作の比較
正常動作しない場合
まず、PureComponentで行われる比較は浅い比較ですので、プロパティにセットしたオブジェクトを破壊的に変更するような場合、比較が一致してしまうので、更新がバイパスされてしまいます。このような場所にPureComponentを使うと、うまく動かなくなってしまいます。
もっとも、Reactのリファレンスでも触れられているように、破壊的変更に頼らないのがReact流です。ということで、PureComponentを使うぐらいにチューニングする以上は、propsやstate以下は破壊的変更を行わない、ということにして先へ進みます。
更新する場合、しない場合
では何でもかんでもPureComponentにしてしまえばいいのかといえば、そういうわけでもありません。単なるComponentとPureComponentを比較すると、以下のような感じになります。
- 比較結果が一致した場合…
PureComponentは仮想DOM関連の処理をバイパスできるので、Componentに対して有利。 - 比較して一致しない場合…
PureComponentは比較を行う分、Componentに対して不利。
ということで、PureComponentの比較で更新をバイパスできない場合は、比較自体の処理分だけコストが乗ってしまうことになります。
ケーススタディ
ルートコンポーネント
Reactでルートとしてrenderする、<App>のようなコンポーネントは、「上部のツリーがrenderされたからそれに合わせて再render」ということが起きない以上、renderが走るとき=内部でstateが変わったとき、です。つまり、PureComponentにしてバイパスできることはありません。比較コストが無駄になるだけです。
同様の理由で、ページを構築するような大規模なコンポーネントも、PureComponentにするのはデメリットのほうが大きいです。
配列のmap
配列をmapしてコンポーネントに変換することも多いですが、「破壊的変更をしない」という前提では中身の変更と同時に配列ごと変更する必要があるので、配列の変更に際してPureComponent化しても意味はありません。
ただ、配列でmapされる子の方をPureComponentにしておけば、配列の中で順番が入れ替わったりしても移動だけで済みますし、効率的に運用できます。このように、コンポーネントを細かく切っておくと便利な場面も、Reactでは多々あります。
関数コンポーネント
stateやref、ライフサイクルメソッドを使わないコンポーネントは関数としても書けますが、propsもシンプルな関数コンポーネントだとPureComponentも相性がいいものです。
ただ、関数コンポーネントのままではPureComponent化できませんが、acdlite/recomposeのpureのような「関数コンポーネントを受け取って、それをrenderするPureComponentを返す関数」(高階コンポーネント、High Order Component)を使うことで、わざわざそのためだけにクラス化しなくてもPureComponentとして活用できるようになります。