React メモ化(React.memo())を利用して再レンダリングを制御する

# メモ化
Reactは`state`や`props`が更新される度に再レンダリングが行われます。
`useCallback()`、`React.memo()`、`useMemo()`を利用すると、初回の処理を実行して記録し、値が必要となった2回目以降は、保持していた計算結果を再利用します。これを**メモ化**といいます。

メモ化は再レンダリングする必要がなくなるため、パフォーマンスの向上を期待できます。

# React.memo()
`React.memo()`はコンポーネントのレンダリング結果をメモ化するReact APIです。親コンポーネントから渡された`props`について前回との変更差分をチェックし、コンポーネントが返した描画結果を記録してメモ化し、差分があった場合のみ再レンダリングを行います。

1. 前回のpropsと新しく親コンポーネントから渡されたpropsを比較し、値が等価であるかチェックする
1. 等価である場合、メモ化したコンポーネントを利用するため、再レンダリングをスキップする
1. 等価でない場合、再レンダリングを行う

また、`React.memo()`でコンポーネントをラップしても、ラップされたコンポーネント内で`useSate()`や`useContext()`を利用している場合、その変化に応じたコンポーネントの再レンダリングが行われます。頻繁に再レンダリングされる親コンポーネントを持つ子コンポーネント以外で利用しても大きなパフォーマンス効果を得ることはできません。

# React.memo（）を使用したサンプル
React.memo()を使用した成果物を作成しました。
再レンダリングが行われる度に、Consoleに変更したラジオボタンリストのメッセージが表示されます。
例えば、ラジオボタンリストAの値を変更した場合、Consoleに「ラジオボタンリストAが選択されました」と出力されます。この時、ラジオボタンリストBの値は変更されていないため、Consoleには出力されません。

https://codesandbox.io/embed/silent-voice-tyud55?fontsize=14&hidenavigation=1&theme=dark

# コード解説
## index.js
2回レンダリングされるのを防ぐためにStrictModeは削除しました。
```javascript
import { createRoot } from "react-dom/client";

import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(<App />);
```

## App.js
```javascript
import React, { useState } from "react";
import "./styles.css";

// ラジオボタン用の要素を持った配列を設定
const items = [
  { id: 1, item: "東京" },
  { id: 2, item: "大阪" },
  { id: 3, item: "愛知" },
  { id: 4, item: "福岡" },
  { id: 5, item: "北海道" },
  { id: 6, item: "宮城" }
];

//　親コンポーネントRadioからprops(name, handleChange, value)を受け取る
const RadioOption = (props) =>

  // map()を用いて配列itemsの要素を１つひとつ設定する
  items.map((item) => {
    return (

      // ラジオボタン内で一意となるkey属性を設定
      <label key={item.id}>
        <input
          type="radio"
          value={item.item}

          // ラジオボタンリストA・Bと２つ存在するため、リストを分けるのにnameを設定する
          name={props.name}

          // ラジオボタンで選択された値を親コンポーネントで定義されている状態変数valueA,valueBに渡す
          onChange={props.handleChange}

          // ラジオボタンの初期値（最初にチェックされている値）を設定する
          // 親コンポーネントから渡されるvalueには初期値にitems[0].item=東京が設定されている
          // そのため、最初にラジオボタンにチェックされているのは東京になる
          checked={item.item === props.value}
        />
        {item.item}
      </label>
    );
  });

// 親コンポーネントRadioからprops(text, value)が渡される
// React．memoでRadioResultコンポーネントをラップする
// 親コンポーネントRadioから渡されたprops(text, value)を記録する
// 2回目以降は前回のprops(text, value)と比較し、等価でない場合のみ再レンダリングを行う
const RadioResult = React.memo((props) => {
  console.log(`${props.text}が選択されました`);
  return (
    <p>
      選択された値:<b>{props.value}</b>
    </p>
  );
});

const Radio = () => {

  // ラジオボタンリストAで現在選択されている値を設定する状態変数valueA
  // valueAを更新するsetValueA
  const [valueA, setValueA] = useState(items[0].item);

  // ラジオボタンリストBで現在選択されている値を設定する状態変数valueB
  // valueBを更新するsetValueB
  const [valueB, setValueB] = useState(items[0].item);

  // ラジオボタンリストAで選択された値(value)を設定する関数handleChangeAを定義
  const handleChangeA = (e) => setValueA(e.target.value);

  // ラジオボタンリストBで選択された値(value)を設定する関数handleChangeBを定義
  const handleChangeB = (e) => setValueB(e.target.value);

  return (
    <>
      <div className="radio-list">
        <div>
          ラジオボタンリストA
          <RadioOption
            name="radioA"
            value={valueA}
            handleChange={handleChangeA}
          />
          <RadioResult text="ラジオボタンリストA" value={valueA} />
        </div>
        <div>
          ラジオボタンリストB
          <RadioOption
            name="radioB"
            value={valueB}
            handleChange={handleChangeB}
          />
          <RadioResult text="ラジオボタンリストB" value={valueB} />
        </div>
      </div>
    </>
  );
};

export default function App() {
  return <Radio />;
}
```
今回の処理は親コンポーネントである`Radio`からReact.memo()でラップされた子コンポーネント`RadioResult`へ`props（text, value）`を渡す。
`text`は固定の文字列のため、変更される値は`value`のみである。
今回のプログラムの構造は以下のようになっていますが、`Radio`コンポーネントから`RadioResult`コンポーネントへ渡される`valueA`の値が変更されても、もう１つの`RadioResult`コンポーネントへ渡される`valueB`の値は変更されないため、ボタンリストAのみ再レンダリングされ、ンソール画面には「ボタンリストAが変更されました」と出力されます。

しかし、React.memo()を外した場合、親コンポーネントから渡される`value`が変更されると、例えボタンリストAのみ変更しても、ボタンリストBを再レンダリングされ、コンソール画面には「ボタンリストAが変更されました」、「ボタンリストBが変更されました」と両方出力されます。

![スクリーンショット 2023-04-02 13.05.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884987/38c488c5-5f92-f0f7-1b99-1456752e5b80.png)

## styles.css
```css
label {
  display: block;
}

.radio-list {
  display: flex;
  justify-content: space-around;
}
```