1
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 Compiler によって自動的にメモ化が行われるそうです。

それ自体は非常にありがたいのですが、手動でメモ化したときと、React Compiler に任せたときとで、パフォーマンスに違いはあるのだろうか?と気になって調べてみました。

実験に使ったコード

import { useState, useMemo, useCallback } from "react";

// Function to perform heavy calculation
const heavyCalculation = (num) => {
  console.time("Calculation time");
  let result = 0;
  for (let i = 0; i < 500000000; i++) {
    result += num;
  }
  console.timeEnd("Calculation time");
  return result;
};

// Component without using useMemo and useCallback
const WithoutMemoization = ({ num }) => {
  const [count, setCount] = useState(0);

  const result = heavyCalculation(num);

  const handleClick = () => {
    setCount(count + 1);
  };

  console.time("Rendering time (Without Memoization)");
  const output = (
    <div>
      <p>Result: {result}</p>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment Count</button>
    </div>
  );
  console.timeEnd("Rendering time (Without Memoization)");

  return output;
};

// Component using only useMemo
const WithUseMemoOnly = ({ num }) => {
  const [count, setCount] = useState(0);

  const result = useMemo(() => heavyCalculation(num), [num]);

  const handleClick = () => {
    console.time("handleClick execution time (useMemo only)");
    setCount(count + 1);
    console.timeEnd("handleClick execution time (useMemo only)");
  };

  console.time("Rendering time (useMemo only)");
  const output = (
    <div>
      <p>Result: {result}</p>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment Count</button>
    </div>
  );
  console.timeEnd("Rendering time (useMemo only)");

  return output;
};

// Component using only useCallback
const WithUseCallbackOnly = ({ num }) => {
  const [count, setCount] = useState(0);

  const result = heavyCalculation(num);

  const handleClick = useCallback(() => {
    console.time("handleClick execution time (useCallback only)");
    setCount((prevCount) => prevCount + 1);
    console.timeEnd("handleClick execution time (useCallback only)");
  }, []);

  console.time("Rendering time (useCallback only)");
  const output = (
    <div>
      <p>Result: {result}</p>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment Count</button>
    </div>
  );
  console.timeEnd("Rendering time (useCallback only)");

  return output;
};

// Component using both useMemo and useCallback
const WithMemoization = ({ num }) => {
  const [count, setCount] = useState(0);

  const result = useMemo(() => heavyCalculation(num), [num]);

  const handleClick = useCallback(() => {
    setCount((prevCount) => prevCount + 1);
  }, []);

  console.time("Rendering time (With Memoization)");
  const output = (
    <div>
      <p>Result: {result}</p>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment Count</button>
    </div>
  );
  console.timeEnd("Rendering time (With Memoization)");

  return output;
};

const App = () => {
  return (
    <div>
      <h1>Test useMemo / useCallback</h1>
      <h2>Without both useMemo and useCallback</h2>
      <WithoutMemoization num={10} />
      <h2>With useMemo only</h2>
      <WithUseMemoOnly num={10} />
      <h2>With useCallback only</h2>
      <WithUseCallbackOnly num={10} />
      <h2>With both useMemo and useCallback</h2>
      <WithMemoization num={10} />
    </div>
  );
};

export default App;
  • useMemouseCallbackもなし
  • useMemoだけ
  • useCallbackだけ
  • useMemouseCallbackもあり

これらの条件で、React 18 と React 19 との違いを見てみます。

Increment Countボタンを 3 回押した分のコンソールの内容を載せています。

React 18

useMemouseCallbackもなし

すべてのパターンにおいて、Strict Mode なので 2 回レンダリングされています。

1回目
handleClick execution time: 2.469970703125 ms
Calculation time: 510.0908203125 ms
Rendering time: 0.02685546875 ms
Calculation time: 474.7578125 ms
Rendering time: 0.028076171875 ms
2回目
handleClick execution time: 0.097900390625 ms
Calculation time: 512.637939453125 ms
Rendering time: 0.0390625 ms
Calculation time: 476.7060546875 ms
Rendering time: 0.030029296875 ms
3回目
handleClick execution time: 0.071044921875 ms
Calculation time: 491.699951171875 ms
Rendering time: 0.0419921875 ms
Calculation time: 475.31787109375 ms
Rendering time: 0.02685546875 ms

useMemoだけ

1回目
handleClick execution time: 0.159912109375 ms
Rendering time: 0.037841796875 ms
Rendering time: 0.022216796875 ms
2回目
handleClick execution time: 0.13916015625 ms
Rendering time: 0.034912109375 ms
Rendering time: 0.01904296875 ms
3回目
handleClick execution time: 0.078125 ms
Rendering time: 0.014892578125 ms
Rendering time: 0.005859375 ms

useCallbackだけ

1回目
handleClick execution time: 0.108154296875 ms
Calculation time: 492.697998046875 ms
Rendering time: 0.02490234375 ms
Calculation time: 474.359130859375 ms
Rendering time: 0.02587890625 ms
2回目
handleClick execution time: 0.1181640625 ms
Calculation time: 510.855224609375 ms
Rendering time: 0.02490234375 ms
Calculation time: 474.216064453125 ms
Rendering time: 0.02880859375 ms
3回目
handleClick execution time: 0.07421875 ms
Calculation time: 497.56396484375 ms
Rendering time: 0.02587890625 ms
Calculation time: 474.2119140625 ms
Rendering time: 0.022216796875 ms

useMemouseCallbackもあり

1回目
handleClick execution time: 0.072021484375 ms
Rendering time: 0.032958984375 ms
Rendering time: 0.01708984375 ms
2回目
handleClick execution time: 0.10302734375 ms
Rendering time: 0.0439453125 ms
Rendering time: 0.028076171875 ms
3回目
handleClick execution time: 0.072021484375 ms
Rendering time: 0.042236328125 ms
Rendering time: 0.01806640625 ms

React 19

アップグレードガイドにある通りに React 本体をアップグレードした後、React Compiler を適用しました。

npm install babel-plugin-react-compiler

今回は Vite を使っていたので、vite.config.jsに設定を追加します。
こちらもスタートガイドにある通りです。

vite.config.js
export default defineConfig(() => {
  return {
    plugins: [
      react({
        babel: {
          plugins: [
            ["babel-plugin-react-compiler"],
          ],
        },
      }),
    ],
  };
});

ReactCompilerConfigは不要だったので指定していません。

useMemouseCallbackもなし

1回目
handleClick execution time: 0.5 ms
Rendering time: 0.0400390625 ms
Rendering time: 0.027099609375 ms
2回目
handleClick execution time: 0.134033203125 ms
Rendering time: 0.0419921875 ms
Rendering time: 0.01708984375 ms
3回目
handleClick execution time: 0.098876953125 ms
Rendering time: 0.033935546875 ms
Rendering time: 0.031982421875 ms

useMemoだけ

1回目
handleClick execution time: 0.114990234375 ms
Rendering time: 0.057861328125 ms
Rendering time: 0.02099609375 ms
2回目
handleClick execution time: 0.119140625 ms
Rendering time: 0.031005859375 ms
Rendering time: 0.01611328125 ms
3回目
handleClick execution time: 0.06689453125 ms
Rendering time: 0.029052734375 ms
Rendering time: 0.027099609375 ms

useCallbackだけ

1回目
handleClick execution time: 0.13818359375 ms
Rendering time: 0.030029296875 ms
Rendering time: 0.014892578125 ms
2回目
handleClick execution time: 0.115966796875 ms
Rendering time: 0.029052734375 ms
Rendering time: 0.01318359375 ms
3回目
handleClick execution time: 0.158935546875 ms
Rendering time: 0.030029296875 ms
Rendering time: 0.033935546875 ms

useMemouseCallbackもあり

1回目
handleClick execution time: 0.115966796875 ms
Rendering time: 0.056884765625 ms
Rendering time: 0.01806640625 ms
2回目
handleClick execution time: 0.055908203125 ms
Rendering time: 0.033203125 ms
Rendering time: 0.031982421875 ms
3回目
handleClick execution time: 0.055908203125 ms
Rendering time: 0.024169921875 ms
Rendering time: 0.009033203125 ms

比較とまとめ

上記の結果の平均値を出し、表にしています。
単位はすべて ms です。

 useMemouseCallbackもなし React 18 React 19
handleClick execution time 0.8796386719 0.2443033854
Calculation time 490.2017415 なし 
Rendering time 0.03214518229 0.03202311198
合計 491.1135254 0.2763264974
 useMemoだけ React 18 React 19
handleClick execution time 0.1257324219 0.1003417969
Rendering time 0.0224609375 0.03035481771
合計 0.1481933594 0.1306966146
 useCallbackだけ React 18 React 19
handleClick execution time 0.1001790365 0.1376953125
Calculation time 487.3173828 なし 
Rendering time 0.0254313151 0.02518717448
合計 487.4429932 0.162882487
 useMemouseCallbackもあり React 18 React 19
handleClick execution time 0.08235677083 0.07592773438
Rendering time 0.03039550781 0.02888997396
合計 0.1127522786 0.1048177083
  • (今回の結果でいえば)すべてを React Compiler に任せるより、手動でメモ化した方がわずかにパフォーマンスが良い
  • 半端にメモ化してある箇所の自動メモ化がスキップされるようなことはなく、ちゃんと最適化されている

上記をもとに、今後のアップグレードを考えるのであれば以下でしょうか。

  • 手動でメモ化してある箇所を、わざわざ外す必要はない
  • メモ化が半端 or 間違っている箇所をわざわざ修正する必要はない
  • (あるのか分からないが)ほんの少しでもパフォーマンスを良くしたい箇所があるなら手動でメモ化しておく

内部実装を読んで書いているわけではないので間違いや「場合による」箇所があるかもしれませんが、どなたかのお役に立てれば幸いです。

1
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
1
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?