工作と競馬2

電子工作、プログラミング、木工といった工作の記録記事、競馬に関する考察記事を掲載するブログ

React自分用メモ 配列の一部要素を更新したときに、更新した要素に依存するコンポーネントだけ再描画する

概要

配列データをstateに持つ親コンポーネントの子として、配列の各要素に依存する子コンポーネントがあるときに、更新した配列要素に依存する子コンポーネントだけを再描画させるためのコーディング方法をメモ。useMemo, useCallbackを用いる。


詳細

があるとき、目的の動作をさせるには、

SampleをMemo化する

必要がある。さらに、Sampleが依存するデータに関数が含まれる場合は、

関数をuseCallbackを使って囲む

必要がある。上記の2点を施し、目的の動作をさせるためのコードを記載する。

準備

  • 必要なものをimport
  • 後で使うtypeを定義しておく
'use client';
import { useState, useCallback, memo, FC } from "react";

type SampleDataType = {
  name: string;
  obj: { objName: string; }
}

type SampleProps = {
  index: number;
  data: SampleDataType;
  updateData: (index: number, item: SampleDataType) => void;
}

Sample

  • MemoSample: Sampleをメモ化
  • ボタンを押したときに、自身が依存するデータを書き換えるため、propsで渡ってきた更新関数props.updateDataを呼ぶ
  • 後述のSampleArrayにて、props.updateDataにわたってくる関数はuseCallbackで囲まれている
  • なお、デバッグ用にconsole.logを仕込んでおく
const Sample: FC<SampleProps> = (props: SampleProps) => {
  console.log(`Sample ${props.index} ${props.data.name} ${props.data.obj.objName}`);

  const onClick = () => {
    const newData = { ...props.data, name: `test${props.index}1`, obj: { objName: `obj${props.index}1` } };
    props.updateData(props.index, newData);
  }

  return (
    <>
      <span>{props.data.name}</span><button onClick={onClick}>変更</button>
    </>
  );
}

// メモ化
const MemoSample = memo(Sample);

配列データをstateに持つ親コンポーネント

  • 配列のデータarrayDataは、実験用に300個の要素を持たせてある
  • 配列内の指定したインデクスの要素を更新する関数updateArrayItemを、useCallbackで囲んだ形で定義する
  • updateArrayItemを、Sampleで呼び出せるようにするため、Sampleのpropsに渡す
  • なお、デバッグ用にconsole.logを仕込んでおく

ポイント2: useCallbackで、

const SampleArray: FC = () => {

  console.log(`SampleArray`);

  // 配列のデータ
  const [arrayData, setarrayData] = useState(
    Array.from({ length: 300 }, (_, i) => ({ name: `test${i}`, obj: { objName: `obj${i}` } }))
  );

  // 配列のインデクスを指定して更新する関数
  // useCallbackで囲っておく
  const updateArrayItem = useCallback((index: number, item: SampleDataType) => {
    setarrayData((prevarrayData) => prevarrayData.map((x, i) => (i === index ? item : x)));
  }, []);

  return (
    <div>
      {arrayData.map(
        (data: SampleDataType, index: number) => 
          <>
            <MemoSample key={index} index={index} data={data} updateData={updateArrayItem} />
            {(index + 1) % 10 === 0 && <br />}
          </>
      )}
    </div>
  );
}

動作確認

まず、コンポーネントを表示させると以下のようになる。

このとき、デバッグ用に仕込んだconsole.logにより以下の出力となり、親コンポーネントと、300個の子コンポーネントが描画される。

SampleArray
page.tsx:18 Sample 0 test0 obj0
:
page.tsx:18 Sample 299 test299 obj299

ここで、113番のボタンを押すと以下のログが出た。すなわち、更新したコンポーネントだけが再描画されている。

page.tsx:37 SampleArray
page.tsx:18 Sample 113 test1131 obj1131