マウスを動かすと再描画する問題の修正
PCブラウザでもっと多く発生するイベントはMouseMove
やScroll
であることが多いです。
そのため、これらのイベントを監視して更新処理をする時は慎重にならないといけません。
これらの頻度が高いイベントを軽減する方法としてdebounceやIntersection Observer
などの新しいAPIがあります。
- Debouncing and Throttling Explained Through Examples | CSS-Tricks
- 入力ハンドラのデバウンス | Web | Google Developers
そもそもの問題として、そのイベントで何も変化する必要がないならばその更新処理を止めるべきです。
観測
視聴ページで、マウスを移動をすると載せただけで更新されるコンポーネントが幾つかあることがわかりました。
facebook/react-devtoolsの"Highlight Updates"を使うことで更新されているコンポーネントが点滅します。
マウス移動で何か表示が変わっているのなら、それは意図した挙動なので問題ありません。 しかし、表示が変わっていないにもかかわらず更新処理(点滅)を行っているならばそれは無駄といえます。
why-did-you-updateを使うことで、無駄な更新処理なのかを確認してみました。 次のような感じでReactをラップすると、無駄な更新をコンソールに表示してくれます。
import * as React from "react";
if (process.env.NODE_ENV !== "production") {
const { whyDidYouUpdate } = require("why-did-you-update");
whyDidYouUpdate(React);
}
ここでの"無駄な更新"とは、全く同じ値(Props)なのに更新(render
)されている処理のことを言います。
- why-did-you-update
- 無駄な更新をコンソールに出力してくれる
- たとえばあるアクションにおいて無駄なレンダリングが起きてるかを調べるときに使う
- react-addons-perf(React 15限定)
- “Wasted” time がwhy-did-you-updateと似た感じ
- 一覧できるので分かりやすい
- アプリを起動時や色んな操作をした結果の統計を見るのに使う
- react-performance
- React 16対応
- 開発中
why-did-you-updateを入れた状態でマウス移動を行う特定のコンポーネントがコンソールログに表示されました。
合わせてコメント欄周辺をマウス移動した時のプロファイルを取ってみます。
先ほどのwhy-did-you-updateとの出力と合わせてみると、Form
コンポーネントの下にある、Input
やButton
やField
といったコンポーネントが無駄に更新されていることがわかります。
問題
次のコンポーネントが同じPropsを受け取ったにもかかわらず更新されているのが問題です。
Input
Button
Field
実装を見るとReact.Component
を継承していて、かつshouldComponentUpdate
が実装されていませんでした。
export class Field extends React.Component {}
この場合はPropsが変わるたびに更新されます。
修正方針
shouldComponentUpdate
を実装して、前回と同じPropsなら更新しないようにすれば解決できそうです。
Note
この問題の修正のために独自のDeepComparisonRenderingOptimizedComponent
という親コンポーネントを使っています。(既存の構造の問題があったため)
通常のケースではReactが提供しているReact.PureComponent
のShallow(浅い)な比較で十分でしょう。
Shallowな比較と、Deepな比較の違いについては次の記事で解説しています。
修正
該当するそれぞれのコンポーネントにshouldComponentUpdate
を実装を追加しました。
計測
Before
マウスを動かすとwhy-did-you-updateのログがでています。
After
マウスを動かしても無駄な更新されるコンポーネントはなくなりました。 why-did-you-updateのログが出なくなった)
具体的には、Form
までupdate
処理が止まっていることがプロファイルからわかります。
つまり、Form
次のコンポーネントにおいてshouldComponentUpdate
により更新が不要と判断され、update
処理が行われなくなっていることを意味します。
Input
Button
Field