Reactのレンダリングの最適化(memo, useCallback, useMemo) @yukilulu0229

## 参考

@soarflat

https://qiita.com/soarflat/items/b9d3d17b8ab1f5dbfed2#reactmemo

## memoを使う場合

https://ja.react.dev/reference/react/memo

```App.jsx
import { useState } from 'react'
import viteLogo from '/vite.svg'
import './App.css'
import ChildArea from "./ChildArea.jsx";

function App() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState("");
  const [isOpen, isSetOpen] = useState(false);

  const onClickCountUp = () => {
    setCount(count + 1);
  };
  const onChangeText = (e) => {
    setText(e.target.value);
  };
  const onClickOpenBox = () => {
    isSetOpen(!isOpen);
  };
  return (
    <div className="App">
      <div>
        <p>{count}</p>
        <button onClick={onClickCountUp}>カウントアップ</button>
      </div>

      <hr />

      <div>
        <div>
          <input type="text" value={text} onChange={onChangeText} />
          <p>{text}</p>
        </div>
      </div>

      <hr />

      <div>
        <button onClick={onClickOpenBox}>表示</button>
        <ChildArea isOpen={isOpen} />
      </div>
    </div>
  )
}

export default App
```

```ChildArea.jsx
import React, {memo} from 'react';
import {createLogger} from "vite";

const style = {
  width: "100%",
  height: "200px",
  background: "gray"
}

const ChildArea = memo((props) => {
  const {isOpen} = props;
  const data = [...Array(2000).keys()]
  console.log("チャイルドエリアがレンダリング");
  data.forEach((index) => {
    console.log(index);
  })
  console.log(data);

  return (
    <div>
      {isOpen ? (
        <p style={style}>isOpen: true</p>
      ) :
        null
      }
    </div>
  );
});

export default ChildArea;

```

カウントアップやonChangeTextを使うたびにChildAreaが再レンダリングされる
これを防ぐために子要素でmemoを使いisOpenに変化がないと再レンダリングされないようにする


## useCallbackを使うとき

関数を子要素に渡すときなど(モーダルの閉じるボタンで閉じたいの真偽値をうまく使うときなど)

https://ja.react.dev/reference/react/useCallback

```App.jsx

import {useCallback, useState} from 'react'
import ChildArea from "./ChildArea.jsx";

function App() {
  const [isOpen, setIsOpen] = useState(false);
  const onClickOpenBox = () => {
    setIsOpen(!isOpen);
  };
  const onclickClose = useCallback(() => {
    setIsOpen(false)
  }, [setIsOpen]);
  return (
    <div className="App">
      <div>
        <button onClick={onClickOpenBox}>表示</button>
        <ChildArea isOpen={isOpen} onclickClose={onclickClose} />
      </div>
    </div>
  )
}

export default App

```

```ChildArea.jsx

import React, {memo} from 'react';

const style = {
  width: "100%",
  height: "200px",
  background: "gray"
}

const ChildArea = memo((props) => {
  const {isOpen, onclickClose} = props;
  const data = [...Array(2000).keys()]
  console.log("チャイルドエリアがレンダリング");
  data.forEach((index) => {
    console.log(index);
  })
  console.log(data);

  return (
    <>
      {isOpen ? (
        <div style={style}>
            <p>isOpen: true</p>
            <button onClick={onclickClose}>閉じる</button>
        </div>
      ) :
        null
      }
    </>
  );
});

export default ChildArea;
```

onclickCloseの部分

今回,親要素自体に変化があるのでonclickCloseが再生成されてしまうので memoとuseCallbackを組み合わせて再生成を制御している

useCallbackには第二引数が必要なので注意する


## useMemo

https://ja.react.dev/reference/react/useMemo

使い所はここを参考にする

https://zenn.dev/chot/articles/react-when-to-use-memo

あまり使う機会は少ない
変数に対して使うといい
繰り返し計算する必要がないというかなんというか

```App.jsx
const testament = useMemo(() => 1 + 2 + 3, [])
```
第二引数が必要