2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

useMemo と React.memo の違い

  • useMemo:値をメモ化
  • React.memo:コンポーネントをメモ化

メモ化はキャッシュのこと。
キャッシュ化は、値を再計算せずに結果だけをどこかに保存しておいて、必要になったらそれを使うことができます。

一方、Reactメモはコンポーネントのみメモ化を行う。

ここが大きな違いで、useMemoはあらゆる値をメモ化しておくことができるので、jsxのコードの部分をメモ化することが可能。

検証

親コンポーネント

Example.jsx
import React, { useCallback, useState } from "react";
import Child from "./Child";

const Example = () => {
  console.log("Parent render");
  const [countA, setCountA] = useState(0);
  const [countB, setCountB] = useState(0);

  const clickHandler = useCallback(() => {
    setCountB(countB + 1);
  },[countB]);
  
  return (
    <div className="parent">
      <div>
        <h3>親コンポーネント領域</h3>
        <div>
          <button
            onClick={() => {
              setCountA((pre) => pre + 1);
            }}
          >
            ボタンA
          </button>
          <span>親のstateを更新</span>
        </div>
      </div>
      <div>
        <p>ボタンAクリック回数:{countA}</p>
      </div>
      <Child countB={countB} onClick={clickHandler}/>
    </div>
  );
};

export default Example;

React.memo を使用した子コンポーネント

Child.jsx
import React from "react";

const Child = React.memo(({ countB, onClick }) => {
  console.log("%cChild render", "color: red;");

  return (
    <div className="child">
      <h2>子コンポーネント領域</h2>
      <button onClick={onClick}>ボタンB</button>
      <p>ボタンBクリック回数:{countB}</p>
    </div>
  );
});

export default Child;

useMemo を使用した子コンポーネント

第二引数に依存配列を設定

Child.jsx
import React from "react";

const Child = ({ countB, onClick }) => {
  console.log("%cChild render", "color: red;");

  useMemo(() => {
  console.log("%cuseMemo", "color: green;")
    return (
      <div className="child">
        <h2>子コンポーネント領域</h2>
        <button onClick={onClick}>ボタンB</button>
        <p>ボタンBクリック回数:{countB}</p>
      </div>
    );
  },[countB]);
};

export default Child;
  • countBが更新された場合には、新しいjsxが返ってくる。
  • それ以外にはメモ化されたjsxが返ってくる。

ボタンAを押したとき、useMemoのconsole.logは表示されない。

  • 注意点:Childコンポーネント自体はレンダーされている。
    • useMemoでメモ化した値がChildコンポーネントの実行結果として返ってきている。

関数を依存配列に含めるケースと含めないケース

関数の中の処理がなんらかのstateに依存している場合は依存配列の中に含めて書く。

Child.jsx
import React from "react";

const Child = ({ countB, onClick }) => {
  console.log("%cChild render", "color: red;");

  useMemo(() => {
    return (
      <div className="child">
        <h2>子コンポーネント領域</h2>
        <button onClick={onClick}>ボタンB</button>
        {/* <p>ボタンBクリック回数:{countB}</p> */} // 依存関係を一旦コメントアウト
      </div>
    );
  },[onClick]); //依存配列をonClick(関数)にする
};

export default Child;

親コンポーネント

Example.jsx
import React, { useCallback, useState } from "react";
import Child from "./Child";

const Example = () => {
  console.log("Parent render");
  const [countA, setCountA] = useState(0);
  const [countB, setCountB] = useState(0);

  const clickHandler = useCallback(() => {
    console.log('countB',countB) //onClickイベントの中で現在のstateの状態をコンソールに追加
    setCountB(countB + 1);
  },[countB]);
  
  return (
    <div className="parent">
      <div>
        <h3>親コンポーネント領域</h3>
        <div>
          <button
            onClick={() => {
              setCountA((pre) => pre + 1);
            }}
          >
            ボタンA
          </button>
          <span>親のstateを更新</span>
        </div>
      </div>
      <div>
        <p>ボタンAクリック回数:{countA}</p>
      </div>
      <Child countB={countB} onClick={clickHandler}/>
    </div>
  );
};

export default Example;

カウントが依存配列に含められているので、カウントのstateが更新されると、まずドラッグした関数が新しい関数で定義される。

  const clickHandler = useCallback(() => {
    console.log('countB',countB) //onClickイベントの中で現在のstateの状態をコンソールに追加
    setCountB(countB + 1);
  },[countB]);

それがclickHandler関数に入って、propsのonClickで子コンポーネントに渡される。
子コンポーネントのuseMemoの依存配列にも入っているので、onClickには、新しく定義され直した関数が格納されていることになる。

この状態だとコンソールでのカウントの値はインクリメントされていく。

スクリーンショット 2023-09-15 7.29.00.png

import React from "react";

const Child = ({ countB, onClick }) => {
  console.log("%cChild render", "color: red;");

  useMemo(() => {
    return (
      <div className="child">
        <h2>子コンポーネント領域</h2>
        <button onClick={onClick}>ボタンB</button>
        {/* <p>ボタンBクリック回数:{countB}</p> */} // 依存関係を一旦コメントアウト
      </div>
    );
  },[]); //依存配列を削除
};

export default Child;

カウントBは0のまま。

スクリーンショット 2023-09-15 7.31.32.png

クリックハンドラーの中は、countBに依存している状態になるので、countBの値が変更されたときに新しく関数を定義しないと、元のcountBを使って処理が行われている状態なので、うまく動かない。

2
2
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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?