useMemo 備忘録

## 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が更新されると、まずドラッグした関数が新しい関数で定義される。

```jsx
  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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288047/fcaab8aa-b502-e1e0-9f7d-35f83044400b.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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3288047/a99aa7f2-416b-8d48-9223-2ba640d4d173.png)


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