React + TypeScript 配列を更新する方法

プログラミング
公開 2024年11月26日

はじめに

初心者向けの内容です。
React で「配列を更新しても表示されないんだけど?」というありがちな問題について簡潔に解決策だけを書きます。

React + TypeScript: 状態に収めた配列を更新する

こちらの記事を参考にしました。詳細な説明についてはこちらを参照してください。

いきなり結論ですが、React では 配列を直接変更しても画面には反映されません
配列に対するどのような変更であっても 新しい配列を作成してセッターに代入 という手法を取る必要があります。

JavaScript(TypeScript)では配列は参照型なので、変更したら参照元にも変更されるのが当たり前ですが、React は違います。配列を変更したかったら四の五の言わずに新しい配列を作って代入しましょう。 場合によっては非常に非効率な(直感に反する)コードを書くことになりますが、このルールが絶対です。

実際のところ、配列に限らずオブジェクト型などの参照型全般に同じことが言えます。

新しく作って代入しろ

イミュータブルこそ神 です。信じましょう。

以下では配列への要素の追加、要素の削除、要素の更新について、どのようなコードを書けばよいのか例示します。

要素の追加

push() しても無意味です。
concat() または スプレッド構文 を使用して、新しい配列を生成し、セッター(setArtists)に代入してください。

setArtists([...artists, { id: nextId++, name }]); 

要素の削除

pop()shift()splice() はすべて無意味です。
filter() または slice() を使用して、新しい配列を生成し、セッター(setArtists)に代入してください。

setArtists(artists.filter(({ id }) => id !== artist.id));

要素の変更

添え字を使用して配列の中身を書き換える行為は無意味です。
map() を使用しましょう。
オブジェクトの配列で1要素の1プロパティを変更する場合だとしても、新しい配列を生成し、セッター(setShapes)に代入してください。

const nextShapes = shapes.map((shape) =>
	shape.type === 'square'
	? {
		...shape,
		type: 'circle',
		color: 'deeppink'
	}
	: {
		...shape,
		y: shape.y + 50
	}
);
setShapes(nextShapes);

配列のソート

reverse()sort() は無意味かつ有害です。
配列のコピーを作ってから、それを並び替え、セッター(setList)に代入してください。

const nextList = [...list];
nextList.reverse();
setList(nextList);

なぜこのような仕組みになっているのか

  • メリット
    • バグの予防
    • 予測可能性の向上
    • 再レンダリングの最適化
    • 時間旅行デバッグ可能
  • デメリット
    • コードが冗長になりがち
    • メモリ使用量の増加
    • 初心者に厳しい

AI に聞くといろいろ教えてくれます。
「バグの予防」という観点は分からなくもないですが、コードが冗長になるのは避けられない感じです。

よくわからなければ、イミュータブルとはつまり 参照型を値型のように扱う と理解しておけば、だいたい合ってると思います。何らかの値を更新するにはセッターを呼び出す必要があるので(値を更新させたくないコンポーネントにはセッターを渡さなければよいので)どこで値が変更されたかを追跡するのは比較的容易かと思います。

参考資料

state 内の配列の更新 – React state 内のオブジェクトの更新