📲

useCallbackについてその機能とパフォーマンスを軽く調査・検証してみた。

2024/09/19に公開

概要

useCallbackの使い方について触れることと、実際にパフォーマンスは向上しているのかを調査するために、「chrome devtool」「React Developer Tools」を使用する。

経緯

社内の人とReactを使ったアプリケーションを作成しているときに、useCallbackが実装されていた。投稿主はこのuseCallbackを「何かをメモするフックだよな」とか「小規模の開発にあんまり使わなさそう」とかのイメージがあった。ただ今後のためにちゃんとした挙動を知らないのもよくないため、これを機会に軽くまとめてみることにした。

環境

  • client OS : Windows 10 Home
  • React : 18.2.0
  • vite : 4.2.0
  • React Developer Tools : 5.3.1

割愛

  • Node.jsのインストール
  • Reactの基礎知識
  • React Developer Toolsの使い方

useCallbackとは

いろいろな記事をみたところ、useCallbackは

  • 関数を記憶しておいてくれる特別なHooks。
  • アプリの動きがスムーズになり、余計な処理を減らすことが可能。
  • React.memoと併用することで使用できる。
  • useMemoと混同されがちだが、useMemoは値などの結果をメモ化するHooksである一方で、useCallbackはコールバック関数をメモ化するHooks。

とあった。
要するに、
アプリケーションのパフォーマンス改善に役立つReactのHooksの一つであり、その機能としてコールバック関数をメモ化する。
といったところ。

実際にパフォーマンス改善がされるかを検証する

様々な記事を参考に、今回以下コードを用意する。

App.jsx
import React,{ useCallback, useState } from 'react';
import './App.css'

const ChildComponent = React.memo(
  ({ onClick }) => {
    console.log("子コンポーネントがレンダリングされました");
    return <button onClick={onClick}>カウントを増やす</button>;
  }
);
function ParentComponent() {

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

  // useCallbackなし
  // const handleClick  = () => { 
  //   console.log("ボタンがクリックされました");
  //   setCount((c) => c + 1);
  //   ;
  // }

  // useCallbackあり
  // const handleClick  = useCallback(() => {
  //   console.log("ボタンがクリックされました");
  //   setCount((c) => c + 1);
  //   ;
  // },[])

  return (
    <div>
      <p>カウント: {count}</p>
      <ChildComponent onClick={handleClick} />
    </div>
  );
}

export default ParentComponent

コメントアウトを切り替えながら、上記を使用。

1.useCallbackなしの部分をコメントアウトを解除。

  • 開発者ツールで確認
    useCallbackなしforなし

レンダリングが行われる度に、「子コンポーネントがレンダリングされました」とコンソールが出力されるため、冗長であることがわかる。

  • React Developer Tools のProfilerで確認
    RDTuseCallbackなしforなし

レンダリングされた親と子のコンポーネントが2つ確認でき、それぞれの再レンダリングの時間がかかっていることがわかる。

2.useCallbackなしの部分を再びコメントアウトして、useCallbackありの部分をコメントアウトを解除。

  • 開発者ツールで確認
    useCallbackありforなし

先ほどとは違い、React.memoでメモ化されたコールバック関数の部分が(「子コンポーネントがレンダリングされました」とコンソールの出力)なくなっていることがわかる。

  • React Developer Tools のProfilerで確認
    RDTuseCallbackありforなし

レンダリングされた親のコンポーネントが1つのみ確認でき、子コンポーネントを再レンダリングしない時間ため、処理が短くなっていることがわかる。

まとめ

小さなプログラムで効果を実感しにくいが、大規模になればなるほど効果を感じやすい機能であることは間違いないし、アプリケーションのパフォーマンス改善に積極的に取り入れる動機になった。

注意

  • returnでjsx/tsxを返すコンポーネント内でReact.memoを使用しても、描画のためのコンポーネントはレンダリングされるのは必然なため、外側にReact.memoを定義した関数を設定する。

引用

https://qiita.com/soarflat/items/b9d3d17b8ab1f5dbfed2#usecallback
https://zenn.dev/t_jima/articles/63d2de08837908
https://nakamuuu.blog/react-how-to-use-usecallback/

Discussion