0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[React] 処理時間を確認するときのReact Developer Tools以外の選択肢

Last updated at Posted at 2024-10-08

はじめに

普段はReactの処理時間などを計測する際は、React Developer Toolsをよく使うと思いますが、<Profiler>でラップすることでも計測できることができます。

やり方

サーバーコンポーネントでAPIなどで初期データを取得し、クライアントコンポーネントのテーブルに渡すことを考えます。クライアントコンポーネントのテーブルでは初期データの表示以外にUPDATEボタンによってテーブルのデータの変更を行いテーブルの再レンダリングも行います。

情報を見たいコンポーネントを<Profiler>で囲うだけで確認できます。
今回はクライアントコンポーネントのテーブルのTableBodyを確認します。

<Profiler id="hoge" onRender={onRender}>{コンポーネント}</ Profiler>

idはプロファイルの識別子です。任意に設定でき、どのプロファイルかを追跡できます。
onRenderはプロファイリング対象のツリー内のコンポーネントが更新された時に呼び出されるコールバック関数です。

例えば下記のようにすることでログを出すことができます
それぞれの値の意味は下記です。

  • id: プロファイラに設定された識別子。

  • phase: レンダリングのフェーズ。初回マウント時は "mount"、再レンダー時は "update" または "nested-update"

  • actualDuration: レンダリングに要した実際の時間(ミリ秒)。

  • baseDuration: 最適化なしでサブツリー全体を再レンダーした場合の推定時間。メモ化している場合は、actualDurationと比較することでメモ化の効果を確認できます。

  • startTime: レンダリングが開始されたタイムスタンプ。

  • commitTime: 更新がコミットされた時刻のタイムスタンプ。

下記ではstartTimecommitTimeの差分を取ることで全体の処理時間を出しています。

function onRender(
  id: string,
  phase: "mount" | "update" | "nested-update",
  actualDuration: number,
  baseDuration: number,
  startTime: number,
  commitTime: number
) {
  console.log(`Profiler ID: ${id}`);
  console.log(`Phase: ${phase}`);
  console.log(`Actual Duration: ${actualDuration} ms`);
  console.log(`Base Duration: ${baseDuration} ms`);
  console.log(`Start Time: ${startTime}`);
  console.log(`Commit Time: ${commitTime}`);
}
page.tsx
import React from "react";
import CustomTable from "./CustomTable";
import { createRows } from "./createData";

const TablePage = async () => {
  const rows = createRows();

  return <CustomTable defaultRows={rows} />;
};
export default TablePage;


CustomTable.tsx
"use client";

import React, { Profiler, useState } from "react";
import {
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Paper,
  Button,
} from "@mui/material";
import { createRows } from "./createRows";
function onRender(
  id: string,
  phase: "mount" | "update" | "nested-update",
  actualDuration: number,
  baseDuration: number,
  startTime: number,
  commitTime: number
) {
  console.log(`Profiler ID: ${id}`);
  console.log(`Phase: ${phase}`);
  console.log(`Actual Duration: ${actualDuration} ms`);
  console.log(`Base Duration: ${baseDuration} ms`);

  const totalDuration = commitTime - startTime;
  console.log(`Start Time: ${startTime} ms`);
  console.log(`Commit Time: ${commitTime} ms`);
  console.log(`Total Duration (全体時間): ${totalDuration} ms`);
}

interface RowType {
  id: number;
  name: string;
  age: number;
}
interface CustomTableProps {
  defaultRows: RowType[];
}
const CustomTable: React.FC<CustomTableProps> = (props) => {
  const { defaultRows } = props;
  const [rows, setRows] = useState(defaultRows);
  const handleClick = () => {
    const newRows = createRows();
    setRows(newRows);
  };
  return (
    <>
      <Button onClick={handleClick}>update</Button>
      <TableContainer component={Paper}>
        <Table>
          <TableHead>
            <TableRow>
              <TableCell>ID</TableCell>
              <TableCell>Name</TableCell>
              <TableCell>Age</TableCell>
            </TableRow>
          </TableHead>
          <Profiler id="hoge" onRender={onRender}>
            <TableBody>
              {rows.map((row) => (
                <TableRow key={row.id}>
                  <TableCell>{row.id}</TableCell>
                  <TableCell>{row.name}</TableCell>
                  <TableCell>{row.age}</TableCell>
                </TableRow>
              ))}
            </TableBody>
          </Profiler>
        </Table>
      </TableContainer>
    </>
  );
};

export default CustomTable;



createRows.ts
const createData = (id: number, name: string, age: number) => ({
  id,
  name,
  age,
});

export const createRows = () =>
  Array.from({ length: 50000 }, (_, index) =>
    createData(index, `Name ${index}`, Math.floor(Math.random() * 100))
  );

結果の確認方法

初期データの表示

初期レンダリング時のconsole.logの結果は下記となりました。
Phasemountとなっていることがわかります

Profiler ID: hoge
CustomTable.tsx:24 Phase: mount
CustomTable.tsx:25 Actual Duration: 10656.60000003688 ms
CustomTable.tsx:26 Base Duration: 9273.400000020862 ms
CustomTable.tsx:29 Start Time: 25299.099999999627 ms
CustomTable.tsx:30 Commit Time: 36181.90000000037 ms
CustomTable.tsx:31 Total Duration (全体時間): 10882.800000000745 ms

UPDATEボタンでの再レンダリング

再レンダリング時のconsole.logの結果は下記となりました。
Phaseupdateとなっていることがわかります。
このデータから
Actual DurationがBase Durationと近い値なのでメモ化により最適化できそうと判断できます。
(今回の場合は、毎回rowsの値が変わるのでメモ化の効果はなさそうです)

Profiler ID: hoge
CustomTable.tsx:24 Phase: update
CustomTable.tsx:25 Actual Duration: 23284.400000032037 ms
CustomTable.tsx:26 Base Duration: 23116.800000015646 ms
CustomTable.tsx:29 Start Time: 43257.90000000037 ms
CustomTable.tsx:30 Commit Time: 66795.59999999963 ms
CustomTable.tsx:31 Total Duration (全体時間): 23537.699999999255 ms

まとめ

今回はを使って処理時間を見てみました。特定のコンポーネントだけサクッと確認したい時などに使えそうです。またReactのドキュメントによるとデフォルトは無効ですか本番環境でも有効にすることが可能なので、ステージングや本番で確認したい時に使えそうです。

参考

https://ja.react.dev/learn/react-developer-tools
https://ja.react.dev/reference/react/Profiler

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?