🌖

【useMemo】React hookが便利すぎる

4 min read

React hookとは..?

React hookはReact16.8から追加された機能で、クラスコンポーネントでしか使用できなかったstateなどのReactの機能を関数コンポーネントで使用できる機能です。
公式ページは以下です。

https://ja.reactjs.org/docs/hooks-intro.html

React hookのAPIについて紹介していこうと思います。
他のReact hookに関するAPIについても解説していますので、そちらもご覧ください。

useMemoとは...?

useMemo()は関数の結果を保持する機能を提供します。保持した内容が同じ場合は再計算をせず保存した値を再利用します。 useCallbackとの違いは、useCallback関数そのものをメモ化しますが、useMemoは関数の戻り値をメモ化します。

使い方

  • useMemoの引数に、関数と依存配列を渡します。
    • 依存配列を空で渡す場合
      • 初期描画のレンダリングのみ、処理が実行されます。
      • 初期に保存された値が常に再利用されるようになります。
    • 依存配列に値を入れる場合
      • 依存配列に指定した値が更新された時に処理を実行します。
  • パフォーマンス最適化のために使用するようにします。
    • 副作用の関数はuseEffectでやるようにします。
const memoizedValue = useMemo(() => {
    computeExpensiveValue(a)
    }, [a]);

実際にコードで書いてみました。

Counter5.jsx
import React, { useMemo, useState } from 'react';
import '../style.css';

const Counter5 = () => {
    const [counter1, setCounter1] = useState(0);
    const [counter2, setCounter2] = useState(100);

    const countUpCounter1 = () => {
        setCounter1(counter1 + 1);
    };
    const countUpCounter2 = () => {
        setCounter2(counter2 + 100);
    };

    const weightFunction = useMemo(() => {
        // 重い処理を実行する。
        let i = 0;
        while (i < 10) {
            i++;
        };

        return counter2 * counter2;
    }, [counter2]);

    return (
        <>
            <p>カウンター1: {counter1}</p>
            <p>カウンター2: {counter2}</p>
            <p>weightFunctionResult: {weightFunction}</p>
            <button onClick={countUpCounter1}>+1</button>
            <button onClick={countUpCounter2}>+100</button>
            <div className='line'></div>
        </>
    );
};

export default Counter5
App.jsx
import React from 'react';
import { Counter } from './components/index';

function App() {
  return (
    <div>
      <p>useMemoのサンプルです</p>
      <Counter5 />
    </div>
  );
}

export default App;

以下のように動作します。

weightFunctionに重い処理の結果をメモ化しており、依存配列にcounter2を指定しています。つまり、counter2が更新されるcountUpCounter2を実行するButtonコンポーネントをクリックした時のみ、それに依存するコンポーネントのみ再レンダリングされます。countUpCounter1を更新するButtonコンポーネントをクリックしてもcounter2は更新されないため、weightFunctionは実行されず、処理は軽いです。

  • useMemoを使用しない場合も試してみました。

以下のように修正しました。

Counter5.jsx
import React, { useMemo, useState } from 'react';
import '../style.css';

const Counter5 = () => {
    const [counter1, setCounter1] = useState(0);
    const [counter2, setCounter2] = useState(100);

    const countUpCounter1 = () => {
        setCounter1(counter1 + 1);
    };
    const countUpCounter2 = () => {
        setCounter2(counter2 + 100);
    };

    const weightFunction = () => {
        // 重い処理を実行する。
        let i = 0;
        while (i < 10) {
            i++;
        };

        return counter2 * counter2;
    };

    return (
        <>
            <p>カウンター1: {counter1}</p>
            <p>カウンター2: {counter2}</p>
            <p>weightFunctionResult: {weightFunction()}</p>
            <button onClick={countUpCounter1}>+1</button>
            <button onClick={countUpCounter2}>+100</button>
            <div className='line'></div>
        </>
    );
};

export default Counter5

以下のように動作します。

weightFunctionに重い処理の結果をメモ化していないので、stateであるcounter1またはcounter2のどちらかが更新されるたびに各コンポーネントが再レンダリングされてしまっています。したがって、両方のButtonコンポーネントによるクリックで再レンダリングが行われているので、描画に時間がかかっています。

まとめ

今回の記事ではuseMemoを紹介しました。次回はuseRefを紹介しようと思います。
githubにサンプルコードを載せていますので、気になった方はご覧ください。

https://github.com/Tomoki-webpro/react-hooks-study

参考記事

https://ja.reactjs.org/docs/hooks-reference.html#usememo

https://qiita.com/seira/items/42576765aecc9fa6b2f8

Discussion

ログインするとコメントできます