[備忘録][Cording][React]コンポーネントをmemo化して効率を上げるときの注意点

## 概要
- 効率的なレンダリングを実現するにはコンポーネントをMemo化する必要がある
- ただし、約束事を守らないとMemo化したつもりでも実際に無駄な再レンダリングが走ることがある
- コンポーネントをMemo化するときの注意点と性質についてメモする

## 結論
**コンポーネントをMemo化するときに知っておくべきこと**
Reactの再レンダリング有無の判定は**shallowEqualで行われる**。よって、objectやlist, functionといったpropsはMemo化しておく必要があり、一方でprimitiveな値はMemo化が不要である。
| props's type              | rendaring behavior            |
| ------------------------- | ----------------------------- |
| primitive(string, number, boolean) | Memo化しなくても良い          |
| object                    | useMemoによるMemo化が必要     |
| list                    | useMemoによるMemo化が必要     |
| function                  | useCallbackによるMemo化が必要 |
| boolean(他の変数を使って作成したもの) | Memo化しなくても良い | 

コンポーネントをMemo化するにあたり意識しておくことまとめ
- Memo化したいコンポーネント(以降ChildComponentとする)をReact.memoでwrappする
- ChildComponentに渡すcallbackやobjectのパラメータを、useCallbackやuseMemoを使ってMemo化する
- primitive型やある変数を使って生成したbool値は特にmemo化は不要


## 簡単なコンポーネントを作り動作確認をしてみる
- ChildA: React.memoなし
- ChildB: React.memoあり
```typescript
'use client';

import React, { useCallback, useMemo } from 'react';
import { useState } from 'react';

// types.ts
interface InputProps {
  value: string;
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
  priString: string,
  priNumber: number,
  priBool: boolean,
  testObj: {
    id: string,
    name: string,
  };
  testBool: boolean,
  testList: number[],
}

interface FormData {
  childA: string;
  childB: string;
}

// ChildA.tsx

const ChildA: React.FC<InputProps> = ({ value, onChange, priString, priNumber, priBool, testObj, testBool, testList }) => {

  console.log("Rendering ChildA.")
  return (
    <div className="flex flex-col gap-2">
      <label 
        htmlFor="childA" 
        className="text-sm font-medium"
      >
        Child A: {priString} {priNumber} {priBool ? "true": "false"} {testObj.id} {testObj.name} {testBool ? "true": "false"} {testList.length}
      </label>
      <input
        id="childA"
        name="childA"
        type="text"
        value={value}
        onChange={onChange}
        placeholder="Enter Child A"
        className="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
      />
    </div>
  );
};

// ChildB.tsx

const ChildB: React.FC<InputProps> = React.memo(({ value, onChange, priString, priNumber, priBool, testObj, testBool, testList }) => {
  console.log("Rendering ChildB.")
  return (
    <div className="flex flex-col gap-2">
      <label 
        htmlFor="childB" 
        className="text-sm font-medium"
      >
        Child B: {priString} {priNumber} {priBool ? "true": "false"} {testObj.id} {testObj.name} {testBool ? "true": "false"} {testList.length}
      </label>
      <input
        id="childB"
        name="childB"
        type="text"
        value={value}
        onChange={onChange}
        placeholder="Enter Child B"
        className="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
      />
    </div>
  );
});


export const Home: React.FC = () => {
  const [formData, setFormData] = useState<FormData>({
    childA: '',
    childB: ''
  });

  const priStirng = "test-text"
  const priNumber = 100
  const priBool = true

  const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>): void => {
    const { name, value } = e.target;
    setFormData((prevData) => ({
      ...prevData,
      [name]: value
    }));
  }, []);

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
    e.preventDefault();
    console.log('Form submitted:', formData);
    // Handle form submission here
  };

  const testObj = useMemo(() => ({
    id: "10",
    name: "Sato"
  }), [])

  const testList = useMemo(() => ([1,2,3,4]), [])

  const testBool = priNumber > 99 && formData.childA == "aaa"

  return (
    <div className="w-full max-w-md mx-auto p-6 bg-white rounded-lg shadow-md">
      <h2 className="text-xl font-bold mb-6">Input Form</h2>
      <form onSubmit={handleSubmit} className="space-y-4">
        <ChildA 
          value={formData.childA}
          onChange={handleInputChange}
          testObj={testObj}
          testList={testList}
          priString={priStirng}
          priNumber={priNumber}
          priBool={priBool}
          testBool={testBool}
        />
        
        <ChildB 
          value={formData.childB}
          onChange={handleInputChange}
          testObj={testObj}
          testList={testList}
          priString={priStirng}
          priNumber={priNumber}
          priBool={priBool}
          testBool={testBool}
        />

        <button 
          type="submit"
          className="w-full bg-blue-500 text-white py-2 px-4 rounded-md hover:bg-blue-600 transition-colors"
        >
          Submit
        </button>
      </form>
    </div>
  );
};

export default Home;

```
- (ケースA) ChildAを更新時、ChildAのみレンダリングされる(ChildBはレンダリングされない)
- (ケースB) ChildBを更新時、ChildAとChildBがレンダリングされる
- handleInputChangeのuseCallbackやtestObjのuseMemoを削除した場合、ケースAにおいてChildAとChildBの両方がレンダリングされる