開発チームがお届けするブログリレーです!
既に公開されている記事もありますので、こちらから他のメンバーの投稿もぜひチェックしてみてください

はじめに

Reactで開発をしてみたけど、「なぜか重い・・・」ってことありませんか?
そんなとき、まずはどの部分にどれくらいの時間がかかっているのかを計測するところから始めるかと思います。

今回は、Reactのパフォーマンス計測方法である以下の2つについて、
実際のコード例をもとに計測の手順を試してみました。

  • React Developer ToolsのProfilerを見る(詳細はこちらを参照)
  • Profiler コンポーネントを使用する(詳細はこちらを参照)

それぞれの使い方、比較した結果をまとめてみたいと思います。

React Developer Tools

概要

React Developer Toolsは、Reactのパフォーマンスを計測することができるブラウザ拡張機能です。

こちらからインストールできます。

インストール後、Reactで構築されたWebサイトにアクセスし、ブラウザの開発者ツールを開くと、
以下のようにComponentsProfiler パネルが表示されるようになります。

 

計測方法

以下のように、レンダリングのたびに重い計算を行うコードで、レンダリング時間を計測してみます。

import "./App.css";
import { useState } from "react";

// 重い計算を行う関数
const heavyCalculation = (number) => {
  console.log("重い計算を実行中...");
  let result = 0;
  for (let i = 0; i < 1000000000; i++) {
    result += number;
  }
  return result;
};

// 重い計算の結果を表示するコンポーネント
const HeavyComponent = () => {
  const result = heavyCalculation(1);
  return <div>計算結果: {result}</div>;
};

function App() {
  const [count, setCount] = useState(0);

  return (
    <div className="App">
      <h2>パフォーマンスの悪い例</h2>
      {/* このカウントをクリックするたびにレンダリングされる */}
      <button onClick={() => setCount(count + 1)}>カウント: {count}</button>
      <HeavyComponent />
    </div>
  );
}
export default App;

開発者ツールのProfilerパネルを開き、左上のRecordボタンをクリックします。

Recordが開始されると以下のようにボタンが赤色になります。
この状態で、計測したい動作を実行します。(今回はカウントボタンをクリックします)

再度Recordボタンをクリックすると、Recordが終了します。
コンポーネントごとに、レンダリングにかかった時間が以下のような形で表示されます。

ここで、上部の「Ranked chart」ボタンをクリックすると、レンダリングの時間が長い順に表示が切り替わります。
今回は「HeavyComponent」に最も時間がかかっており、
「HeavyComponent」のバーにマウスを重ねると、以下のように画面のどの部分にあたるかがわかります。

また、「HeavyComponent」の横に表示されている時間が、レンダリングにかかった時間になります。
今回は776.1msかかっていることがわかりました。

改善する

無駄な計算をスキップするために、useMemoを使って関数をメモ化してみます。

useMemoは、計算結果をキャッシュすることができるReact Hookです。
※useMemoの詳細はこちらを参照。

以下が改善後のコードです。

import "./App.css";
import { useState, useMemo } from "react";

// 重い計算を行う関数
const heavyCalculation = (number) => {
  console.log("重い計算を実行中...");
  let result = 0;
  for (let i = 0; i < 1000000000; i++) {
    result += number;
  }
  return result;
};

// 重い計算の結果を表示するコンポーネント(Memo化)
const HeavyComponent = () => {
  const result = useMemo(() => heavyCalculation(1), []);    // ここを変更
  return <div>計算結果: {result}</div>;
};

function App() {
  const [count, setCount] = useState(0);

  return (
    <div className="App">
      <h2>パフォーマンスの悪い例</h2>
      <button onClick={() => setCount(count + 1)}>カウント: {count}</button>
      <HeavyComponent />
    </div>
  );
}

export default App;

同じように時間を計測してみます。


「HeavyComponent」のレンダリング時間が776.1ms → 0.3msになりました!

Profilerコンポーネント

概要

以下のように、計測したいコンポーネントをProfilerでラップすることで、パフォーマンスを計測することができます。

<Profiler id="App" onRender={onRender}>
  <App />
</Profiler>

計測方法

先ほどの改善後のコードに対し、以下を追加します。

  • パフォーマンス計測用のコールバック関数(onRender)
  • Profilerコンポーネント

コードは以下のようになります。

import "./App.css";
import { useState, useMemo, Profiler } from "react";

// 重い計算を行う関数
const heavyCalculation = (number) => {
  console.log("重い計算を実行中...");
  let result = 0;
  for (let i = 0; i < 1000000000; i++) {
    result += number;
  }
  return result;
};

// 重い計算の結果を表示するコンポーネント(Memo化)
const HeavyComponent = () => {
  const result = useMemo(() => heavyCalculation(1), []);
  return <div>計算結果: {result}</div>;
};

// パフォーマンス計測用のコールバック関数
const onRender = (id, phase, actualDuration) => {
  console.log(
    `ID:${id} phase:${phase} レンダリング時間:${actualDuration}ミリ秒`
  );
};

function App() {
  const [count, setCount] = useState(0);

  return (
    <div className="App">
      <h2>パフォーマンスの悪い例</h2>
      <button onClick={() => setCount(count + 1)}>カウント: {count}</button>
      {/* Profilerでラップする */}
      <Profiler id="HeavyComponent" onRender={onRender}>
        <HeavyComponent />
      </Profiler>
    </div>
  );
}

export default App;

Profilerのidには対象のコンポーネント名、onRenderにはレンダリング情報を表示するための処理を定義します。
今回は、以下の情報をコンソールログに表示してみました。

  • 計測対象のコンポーネント(ID)
  • 初回のマウント(mount) or 再レンダリング(update)
  • レンダリング時間(ms)

※onRenderで取得できる情報については、こちらを参照。

結果

カウントボタンを数回クリックすると、以下のようにコンソールログが表示されました。


初回のレンダリングのみ時間がかかっていますが、2回目以降はuseMemoの効果で時間が短縮できていることが分かります。

まとめ

それぞれの方法についてまとめます。

React Developer Tools:
拡張機能のインストールが必要になるが、
各コンポーネントのレンダリング時間を視覚的に確認できるので、ボトルネックを探しやすい。

Profilerコンポーネント:
コード修正が必要になるが、
インストールなどは不要で、好きなフォーマットでログに情報を出力することができる。

以上より、
まずはReact Developer Toolsを使ってボトルネックとなっているコンポーネントを特定し、
その後、Profilerコンポーネントを使って特定のコンポーネントのレンダリング情報の詳細を確認する
という使い方がいいかなと思います。

どちらも手軽にできるので、ぜひ試してみてください!