1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-11-18

概要

  • 効率的なレンダリングを実現するにはコンポーネントを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あり
'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の両方がレンダリングされる
1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?