Aqutras Members' Blog

株式会社アキュトラスのメンバーが、技術情報などを楽しく書いています。

Reactコンポーネントが無駄に再レンダリングされていないか調べる

こんにちは。maxmellon です。

今回は,Reactで作られたアプリケーションの無駄な再レンダリング回数を 簡単に調査するためのツールを一つ紹介したいと思います。

Reactの基本的なことにつきましては,下記を参照してください.

https://facebook.github.io/react/docs/why-react.html http://mizchi.hatenablog.com/entry/2014/09/02/201728

はじめに

無駄な再レンダリングとは何か?

要するに,DOMの中身が全く同じなのに render() が走ってしまうことです.

具体的には

exports default () => (
  <div>
    <p>{this.state.name}</p>
  </div>
)

上記のようなReactComponent があったとして,再レンダリングを期待するのは, state の name が変更された時です.

それはすなわち, this.setState({ name: '' }) された時です.

無駄なレンダリングが行われている例

this.state.name に, hoge という文字列が入っていたとして, this.setState({ name: 'hoge' }) されたとします.

これは,再レンダリングされるべきでしょうか? されるべきではないと思います.なぜなら state の中身が同じで DOM を作りなおす意味が無いからです.


別のケースを考えてみましょう.

仮に,this.state.name に, hoge という文字列が入っていたと仮定しましょう

その時,this.setState( { name: 'foo' } ) とされると,stateが書き換わり, 再レンダリングされます.これは,name が hoge から foo に書き換わったので 無駄なレンダリングではありません. 今回は,こういった無駄な再レンダリングの存在を,簡単に調べることのできる, モジュールを紹介したいと思います.

why-did-you-update とは

さて,本題に入るのですが,why-did-you-update というモジュールを紹介したいと思います これは,React に対してモンキーパッチを当てて,React.Component.prototypeshouldComponentUpdate に props と state の差分がないかどうか計測する関数を実装し,これから行われるレンダリングが無駄かどうかの判定を行っています.

注意するべき点が,すでに sholdComponentUpdate が実装されている場合, 判定することができないという点です. (すでに定義されていた場合上書きしないようにライブラリ側で抑制されている) 参考 : https://youtu.be/C8bMahvTkHA?t=20m

使用感

使い方は非常にシンプルで,React に対して次のようにするだけでよいです.

import React from 'react'
import {whyDidYouUpdate} from 'why-did-you-update'

whyDidYouUpdate(React)

こうすることでブラウザのコンソールに次のように表示されます

実際に使ってみる

アキュトラスでは,renderされるComponent数が非常に多いアプリケーションを開発しています.

ざっくりとどれほどの規模かというと,

  • シングルページ
  • コンポーネントの種類が 40 程度
  • コンポーネント数が数百〜数千程度 (データによって上下)
  • Store の種類が 20 程度

(ざっと感覚で数えたものなので正確な数字ではありません)

アプリケーションがかなり大規模になってきて,最近はパフォーマンスが だいぶ気になってきています.上記のものが1ページで全て読み込まれるので ページ読み込み後の初回レンダリングが,すごく遅く悩まされています.

今回,その悩みを解決するための調査として,why-did-you-update を使って このアプリケーションに対して調査してみたいと思います

今回は、問題となっている初回レンダリングにどれほどの無駄があるかを調べたいと思います。

ページにアクセスして何も操作せず,すべてのコンポーネントのレンダリングが 完了したときの回数を調べたいとおもいます.

調査の結果 25381 回 無駄な可能性が高い,再レンダリングが行われている 可能性がありました.

なぜここまで回数が増大したのかを考えると,親コンポーネントに対して無駄な 再レンダリングがかかり,親が持っている子コンポーネントすべてに再レンダリングが 走ったためかと考えます.

個人的に注目した点

ループで作成した,テーブルの要素がすべて無駄な再レンダリングの可能性があると 表示されていたので,(親が無駄な再レンダリングを行っていたため) 繰り返し使うようなコンポーネントから,shouldUpdateComponentを 実装していくべきなのかもしれないと思いました.

次回予告

次回は,実際に無駄なレンダリングを減らすことでどれほどパフォーマンスが向上するのか, この調査結果を元に,他にもボトルネックになっているものはないか, 調査していきたいと考えております.

また,汎用的な shouldUpdateComponent の実装例などについて触れていきます