React useStateで一覧の追加と削除を行う
Reactで一覧を表示するときは、配列をuseStateへ保存できます。
今回は簡単な買い物リストを作り、項目の追加と削除を行います。
配列のpushやspliceで既存のstateを直接変更するのではなく、追加後・削除後の新しい配列を作ります。Reactはstateの参照が変わったことを更新の手掛かりにするためです。また、削除対象は配列の位置ではなく、項目の安定したIDで指定します。
配列をuseStateへ保存する
一覧の項目を表す型を定義し、初期値を指定します。
ShoppingList.tsximport { useState } from 'react';
type ShoppingItem = {
id: number;
name: string;
};
export const ShoppingList = () => {
const [items, setItems] = useState<ShoppingItem[]>([
{ id: 1, name: 'りんご' },
{ id: 2, name: '牛乳' },
]);
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
};ShoppingItem[]は、ShoppingItemの配列という意味です。
mapで配列の各項目をli要素へ変換します。keyには一覧内で重複しない値を指定します。
入力した項目を追加する
入力欄の値もstateへ保存し、追加ボタンで新しい配列を作ります。
項目を追加する処理const [newItemName, setNewItemName] = useState('');
const handleAdd = () => {
const name = newItemName.trim();
if (!name) return;
const newItem: ShoppingItem = {
id: Date.now(),
name,
};
setItems((current) => [...current, newItem]);
setNewItemName('');
};...currentで現在の項目を新しい配列へコピーし、末尾へnewItemを追加します。
この例では説明を簡単にするため、現在時刻をIDにしています。データベースへ保存するアプリケーションでは、登録APIが返すIDを使用します。
入力欄とボタンは以下のようにします。
<input
value={newItemName}
onChange={(event) => setNewItemName(event.target.value)}
placeholder="商品名"
/>
<button type="button" onClick={handleAdd}>
追加
</button>選択した項目を削除する
filterを使い、削除するID以外の項目で新しい配列を作ります。
項目を削除する処理const handleDelete = (id: number) => {
setItems((current) => current.filter((item) => item.id !== id));
};一覧の各項目へ削除ボタンを追加します。
<ul>
{items.map((item) => (
<li key={item.id}>
{item.name}
<button type="button" onClick={() => handleDelete(item.id)}>
削除
</button>
</li>
))}
</ul>ボタンが押された項目のidをhandleDeleteへ渡します。
完成したコンポーネント
追加と削除をまとめると以下のようになります。
ShoppingList.tsximport { useState, type FormEvent } from 'react';
type ShoppingItem = {
id: number;
name: string;
};
export const ShoppingList = () => {
const [items, setItems] = useState<ShoppingItem[]>([]);
const [newItemName, setNewItemName] = useState('');
const handleAdd = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const name = newItemName.trim();
if (!name) return;
setItems((current) => [
...current,
{ id: Date.now(), name },
]);
setNewItemName('');
};
const handleDelete = (id: number) => {
setItems((current) => current.filter((item) => item.id !== id));
};
return (
<section>
<h1>買い物リスト</h1>
<form onSubmit={handleAdd}>
<input
value={newItemName}
onChange={(event) => setNewItemName(event.target.value)}
placeholder="商品名"
/>
<button type="submit">追加</button>
</form>
{items.length === 0 ? (
<p>項目はありません。</p>
) : (
<ul>
{items.map((item) => (
<li key={item.id}>
{item.name}
<button
type="button"
onClick={() => handleDelete(item.id)}
>
削除
</button>
</li>
))}
</ul>
)}
</section>
);
};元の配列を直接変更しない
以下のようにpushしただけでは、Reactが変更を検出できない場合があります。
避ける例items.push(newItem);追加にはスプレッド構文、削除にはfilterを使い、新しい配列をsetItemsへ渡します。
useStateの値はメモリ上にあります。ページを再読み込みすると初期値へ戻ります。
値を残す場合は、APIを使ってデータベースへ保存する方法や、用途に応じてlocalStorageへ保存する方法を使用します。
配列のstateを更新するときは、現在の配列を直接変更せず、新しい配列を作ることがポイントです。