Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
14
Help us understand the problem. What are the problem?

【React】レンダリングの最適化にReact.memoとuseCallbackとReact.useMemoを使う

レンダリングの最適化にmemoとuseCallbackとReact.useMemoを使う

Reactの開発は、再レンダリングを意識しながら開発をすることで、パフォーマンスの改善につなげることができます。
再レンダリングを制御するためにメモ化を行います。メモ化する技術のReact.memoとuseCallbackとReact.useMemoをまとめていきます。

メモ化の種類

Reactはメモ化することで、再レンダリングを制御・最適化することができます。
メモ化は前回の処理結果を保持しておくことで、処理を高速化することができます。

種類 対象
React.memo コンポーネント
useCallback コールバック関数
React.useMemo 変数

再レンダリングが起きる条件

  1. Stateが更新されたコンポーネント
  2. Propsが変更されたコンポーネント
  3. 再レンダリングされたコンポーネント配下のすべてのコンポーネント

 再レンダリングを起こしてみる

/src/index.js
import ReactDom from "react-dom";
import { App } from "./App";

ReactDom.render(<App />, document.getElementById("root"));
);

/src/App.jsx
import { useState, useCallback } from "react";
import { Child1 } from "./components/Child1";
import { Child4 } from "./components/Child4";

export const App = () => {
  console.log("App レンダリング ");

  const [num, setNum] = useState(0);

  const onClickButton = () => {
    setNum(num + 1);
  };

  const onClickReset = useCallback(() => {
    setNum(0);
  }, []);

  return (
    <>
      <button onClick={onClickButton}>ボタン</button>
      <p>{num}</p>
      <Child1 onClickReset={onClickReset} />
      <Child4 />
    </>
  );
};
/src/components/Child1.jsx
import { Child2 } from "./Child2";
import { Child3 } from "./Child3";

const style = {
  backgroundColor: "#AAFFFF ",
};

export const Child1 = (props) => {
  console.log("Child1 レンダリング ");

  const { onClickReset } = props;

  return (
    <div style={style}>
      <p>Child1</p>
      <button onClick={onClickReset}>リセット</button>
      <Child2 />
      <Child3 />
    </div>
  );
};

jsx/src/components/Child2.jsx
const style = {
  backgroundColor: "#FFCCFF",
};

export const Child2 = () => {
  console.log("Child2 レンダリング ");

  return (
    <div style={style}>
      <p>Child2</p>
    </div>
  );
};
jsx/src/components/Child3.jsx
const style = {
  backgroundColor: "#FFCCFF",
};

export const Child3 = () => {
  console.log("Child3 レンダリング ");

  return (
    <div style={style}>
      <p>Child3</p>
    </div>
  );
};
jsx;/src/components/Child4.jsx
const style = {
  backgroundColor: "#66FF00",
};

export const Child4 = () => {
  console.log("Child4 レンダリング ");

  return (
    <div style={style}>
      <p>Child4</p>
    </div>
  );
};
FireShot Capture 085 - React App - localhost.png

上記のコードで実行した後、カウントアップのボタンを押すと下記のようにすべてのコンポーネントが再レンダリングされていることが確認できます。

スクリーンショット 2021-09-30 23.38.25.png

コンポーネントのメモ化にReact.memoを使う

React.memoは、コンポーネントをメモ化します。上記のコードのコンポーネントをReact.memoを使うように変更します。

React.memoの書式
const Component = memo(() =>{});
/src/App.jsx
import { useState, memo, useCallback } from "react";
import { Child1 } from "./components/Child1";
import { Child4 } from "./components/Child4";

export const App = memo(() => { // memoに変更した
  console.log("App レンダリング ");

  const [num, setNum] = useState(0);

  const onClickButton = () => {
    setNum(num + 1);
  };

  const onClickReset = useCallback(() => {
    setNum(0);
  }, []);

  return (
    <>
      <button onClick={onClickButton}>ボタン</button>
      <p>{num}</p>
      <Child1 onClickReset={onClickReset} />
      <Child4 />
    </>
  );
});
jsx/src/components/Child1.jsx
import { memo } from "react";
import { Child2 } from "./Child2";
import { Child3 } from "./Child3";

const style = {
  backgroundColor: "#AAFFFF ",
};

export const Child1 = memo((props) => { // memoに変更した
  console.log("Child1 レンダリング ");

  const { onClickReset } = props;

  return (
    <div style={style}>
      <p>Child1</p>
      <button onClick={onClickReset}>リセット</button>
      <Child2 />
      <Child3 />
    </div>
  );
});

jsx/src/components/Child2.jsx
import { memo } from "react";

const style = {
  backgroundColor: "#FFCCFF",
};

export const Child2 = memo(() => { // memoに変更した
  console.log("Child2 レンダリング ");

  return (
    <div style={style}>
      <p>Child2</p>
    </div>
  );
});
jsx/src/components/Child3.jsx
import { memo } from "react";

const style = {
  backgroundColor: "#FFCCFF",
};

export const Child3 = memo(() => { // memoに変更した
  console.log("Child3 レンダリング ");

  return (
    <div style={style}>
      <p>Child3</p>
    </div>
  );
});
jsx;/src/components/Child4.jsx
import { memo } from "react";

const style = {
  backgroundColor: "#66FF00",
};

export const Child4 = memo(() => {
  console.log("Child4 レンダリング ");

  return (
    <div style={style}>
      <p>Child4</p>
    </div>
  );
});
FireShot Capture 086 - React App - localhost.png

メモ化したことで、Appコンポーネントのみ再レンダリングされていることが確認することができました。

スクリーンショット 2021-10-01 0.03.14.png

関数による再レンダリングを起こしてみる

カウンアップをリセットするボタンの関数を作成し、関数をChild1に渡すコードを作成しました。実行し、カウントアップを行うと先程メモ化した、Chile1の再レンダリングが発生してしまいます。

FireShot Capture 087 - React App - localhost.png
スクリーンショット 2021-10-01 23.46.59.png
/src/App.jsx
import { useState, memo } from "react";
import { Child1 } from "./components/Child1";
import { Child4 } from "./components/Child4";

export const App = memo(() => {
  console.log("App レンダリング ");

  const [num, setNum] = useState(0);

  const onClickButton = () => {
    setNum(num + 1);
  };

// 追加しました
  const onClickReset = () => {
    setNum(0);
  };

  return (
    <>
      <button onClick={onClickButton}>ボタン</button>
      <p>{num}</p>
      <Child1 onClickReset={onClickReset} /> // 関数の受け渡しを追加しました
      <Child4 />
    </>
  );
});

jsx/src/components/Child1.jsx
import { memo } from "react";
import { Child2 } from "./Child2";
import { Child3 } from "./Child3";

const style = {
  backgroundColor: "#AAFFFF ",
};

export const Child1 = memo((props) => {
  console.log("Child1 レンダリング ");

  const { onClickReset } = props;

  return (
    <div style={style}>
      <p>Child1</p>
      <button onClick={onClickReset}>リセット</button>
      <Child2 />
      <Child3 />
    </div>
  );
});

関数のメモ化にuseCallbackを使ってみる

Reactの関数のメモ化は、useCallbackを使います。onClickReset関数を下記のようにuseCallbackで書きます。第1引数に関数を書き、第2引数には依存配列の要素を書きます。[ ]の場合は、レンダリングされた最初の1回のみ読み込まれます。2回目からは最初に作成されたものが使われます。

/src/App.jsx
import { useState, memo, useCallback } from "react";
import { Child1 } from "./components/Child1";
import { Child4 } from "./components/Child4";

export const App = memo(() => {
  console.log("App レンダリング ");

  const [num, setNum] = useState(0);

  const onClickButton = () => {
    setNum(num + 1);
  };

  const onClickReset = useCallback(() => { //useCallbackを使うように変更した
    setNum(0);
  }, []);

  return (
    <>
      <button onClick={onClickButton}>ボタン</button>
      <p>{num}</p>
      <Child1 onClickReset={onClickReset} />
      <Child4 />
    </>
  );
});

Child1が、再レンダリングされなくなりました。

スクリーンショット 2021-10-01 23.59.00.png

変数のメモ化にReact.useMemoを使う

変数や値のメモ化には、React.useMemoを使います。第1引数に計算式、第2引数に依存配列の要素を書きます。依存配列の要素のいずれかが変化した場合にのみメモ化された値を再計算します。下記のコードの場合、aとbの値が変わらない場合、最初にレンダリングした時の値が使い回されます。

React.useMemo
const memoizedValue = useMemo(() => {
    return a + b;
    }, [a, b]);
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
14
Help us understand the problem. What are the problem?