何番煎じになるかはわからないですが、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
として活用できるようになります。