LoginSignup
19
11

More than 1 year has passed since last update.

【React】useCallbackとuseMemoによる表示パフォーマンス改善

Last updated at Posted at 2021-05-11

以前、React.memoによるReactの表示パフォーマンスの改善についてまとめました。
それに引き続き、今回はReact.memoとあわせて使われるHooksである、useCallbackuseMemoについて調べ、レンダリングの挙動について検証しました。

useCallback

useCallbackはメモ化されたコールバック関数を返すReact Hooksです。
インラインのコールバック関数とそれが依存している値の配列を渡すと、useCallbackはそのコールバック関数をメモ化したものを返し、その関数は依存配列の要素のいずれかが変化した場合にのみ変化します。

useCallbackは主に親コンポーネントで使用し、メモ化されたコールバック関数は子コンポーネントのpropsとして渡します。
子コンポーネントをReact.memoでエクスポートしていれば、propsの変化がない限り余計なレンダリングは起こりません。
このようにして、Reactの表示パフォーマンスを向上させてくれます。

検証

Log nameを押すとconsole.logに文言が出力され、Increase Count1(2)を押すとカウントが表示されるような画面をつくりました。
レンダリング時にfunctionslogName関数が追加されるようにし、useCallbackの使用有無でその挙動がどのように変わるのかを調べました。
スクリーンショット 2021-05-11 21.42.40.png

useCallbackを使わない場合

画面の実装は以下のようになります。

App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';

const functions = new Set();

const App = () => {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);

  const incrementCount1 = () => setCount1(count1 + 1);
  const incrementCount2 = () => setCount2(count2 + 1);

  const logName = () => console.log('Yihua');

  functions.add(logName); //レンダリングの度にfunctionsインスタンスにlogName関数が追加される

  console.log(functions);

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        Count1: {count1}
        <button onClick={incrementCount1}>Increase Count1</button>
        Count2: {count2}
        <button onClick={incrementCount2}>Increase Count2</button>
        <button onClick={logName}>Log name</button>
      </header>
    </div>
  );
};

export default App;

Increase Count1(もしくは2)を押すとState(count1, count2)の変化が起こるため、カウントが増える度に再レンダリングされます。
その結果、レンダリングの度にfunctionsにはlogName関数が追加されています。
スクリーンショット 2021-05-11 21.56.30.png

useCallbackを使う場合

const logName = () => console.log('Yihua');と記述していた箇所をuseCallbackで書き換えます。
このとき、第2引数には空配列を与えているので、初期表示でメモ化を行った後に関数の再生成は起こりません。

App.js
  const logName = useCallback(() => console.log('Yihua'), []);

その結果、Increase Count1(2)を何回押しても、functionsにはlogName関数は追加されていきません(Set(1)じゃなかった理由は調査中)
スクリーンショット 2021-05-11 21.58.50.png

useMemo

useCallbackではコールバック関数のメモ化を行いましたが、useMemoは値のメモ化を行います。

“作成用” 関数とそれが依存する値の配列を渡し、依存配列の要素のいずれかが変化した場合にのみメモ化された値を再計算します。
この最適化によりレンダリングごとにコストの高い計算が実行されるのを避けることができます。

useMemouseCallbackと同様に、React.memoとあわせて使われます。

検証

Increase Count1を押したときだけdoSomethingComplicated関数が実行され、計算結果がcomplexValueとして表示されるような画面を実装しました。
スクリーンショット 2021-05-11 22.26.58.png

useMemoを使わない場合

doSomething関数を追加して、complexValue: {doSomethingComplicated()}として表示します。

App.js
  //レンダリングの度に実行される
  const doSomethingComplicated = () => {
    console.log('I am computing something complex');
    return ((count1 * 1000) % 12.4) * 51000 - 4000;
  };

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        Count1: {count1}
        <button onClick={incrementCount1}>Increase Count1</button>
        Count2: {count2}
        <button onClick={incrementCount2}>Increase Count2</button>
        complexValue: {doSomethingComplicated()}
        <button onClick={logName}>Log name</button>
      </header>
    </div>
  );

するとレンダリングのたび(Increase Count1だけではなくIncrease Count2を押しても)にdoSomethingComplicated内のconsole.logが実行されます。
スクリーンショット 2021-05-11 22.28.46.png

useMemoを使う場合

doSomethingComplicateduseMemoで書き換えると以下のようになります。
第2引数に与えたcount1の値が変化したときだけ新しい値を返すようになります。

  const doSomethingComplicated = useMemo(() => {
    console.log('I am computing something complex');
    return ((count1 * 1000) % 12.4) * 51000 - 4000;
  }, [count1]);

また、doSomethingCompletedは値を返すようになるので、以下の箇所も書き換えます。

complexValue: {doSomethingComplicated}

こうすることで、Increase Count2をいくら押してもdoSomethingCompletedは実行されないようになります。

参考資料

19
11
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
19
11