当サイトは、アフィリエイト広告を利用しています

【ReactHooks】useStateでオブジェクト配列を扱う

作成日:2022月04月20日
更新日:2024年02月06日

useStateでオブジェクト配列をstateに設定した場合の
操作をまとめる。

useStateの基本的な使い方については下記記事で紹介していますので
初歩的なところから知りたい場合はご参照ください!

全体のソース

簡単なtodoリストを作成する画面
機能としては下記ができる。

  • todoの新規追加
  • todoの更新
  • todoの削除
App.js
import "./styles.css";
import React, { useState, useRef } from "react";
export default function App() {
// 初期値
const initialState = [];
// 登録input
const registInput = useRef(null);
// useState
const [list, setList] = useState(initialState);
// 更新内容input入力時のstate更新
//リストから更新対象のオブジェクトのtargetプロパティのみを更新する
const inputUpdate = (event, i) => {
setList(
// mapで更新対象オブジェクトのみを変更した新規配列を作成し、stateにセットする
list.map((val, index) =>
index === i ? { ...val, target: event.target.value } : { ...val }
)
);
};
// todolistのクリア
const clear = () => setList(initialState);
// todolistへの追加
const add = () => {
// 値がない場合は追加しない
if (!registInput.current.value) return;
setList((prevList) => [
...prevList,
{ title: registInput.current.value, target: "" }
]);
};
// todolistの更新
const upd = (i) => {
setList(
// 更新内容inputを変更した時点でstateの配列に格納されているオブジェクトのtargetは
// 更新されているため、更新ボタン押下時は更新対象オブジェクトのterget→titleで更新した
// 新規配列をmapで作成し、stateにセットする
list.map((val, index) =>
index === i ? { ...val, title: val.target } : { ...val }
)
);
};
// todolistの削除
const del = (i) => {
setList(list.filter((x, index) => index !== i));
};
return (
<div className="App">
<h1>todolistの削除</h1>
<div>
<input type="text" ref={registInput}></input>
<button className="button" onClick={add}>
登録
</button>
<button className="button" onClick={clear}>
クリア
</button>
</div>
<h1 style={{ textAlign: "left" }}>ToDo List</h1>
<div style={{ textAlign: "left" }}>
{list.map((todoList, i) => (
<li key={i}>
<button className="button" onClick={() => del(i)}>
削除
</button>
{todoList.title}
<input
type="text"
value={todoList.target}
onChange={(e) => inputUpdate(e, i)}
></input>
<button className="button" onClick={() => upd(i)}>
更新
</button>
</li>
))}
</div>
</div>
);
}

画面

useState2

useStateの作成

初期値としてstateに配列を設定する。

App.jsx
// 初期値
const initialState = [];
// useState
const [list, setList] = useState(initialState);

登録処理

inputに値を入力し、登録ボタンを押下したらtodoが追加される機能を実装する。

useRefでinputと紐づけ

登録ボタン時にinputに入力されている値を取得するために
useRefでinputの値をregistInputで保持するようにしておく。
※useStateでも可能

App.jsx
// 登録input
const registInput = useRef(null);
// ~省略~
<input
type="text"
ref={registInput}
></input>

add処理

inputで入力した値を、オブジェクトにしてsetStateで追加する
※追加と書いたが実際は新しいオブジェクトが追加された新しい配列を生成している

App.jsx
// todolistへの追加
const add = () => {
// 値がない場合は追加しない
if (!registInput.current.value) return;
setList((prevList) => [
// 直前のstateをスプレッド構文で展開
...prevList,
// 新規オブジェクトを追加
{ title: registInput.current.value, target: "" }
]);
};

配列の追加位置

下記ようにsetState内の位置を入れ替えれば
追加するtodoを最下部か最上部のどちらに入れるか変更できる

App.jsx
// todolistへの追加
const add = () => {
// 値がない場合は追加しない
if (!registInput.current.value) return;
setList((prevList) => [
// 新規オブジェクトを追加
{ title: registInput.current.value, target: "" },
// 直前のstateをスプレッド構文で展開
...prevList
]);
};

更新処理

inputに入力した値で更新ボタン押下時に対象のtodoを更新する

App.jsx
// ~
<input
type="text"
value={todoList.target}
onChange={(e) => inputUpdate(e, i)}
></input>
<button className="button" onClick={() => upd(i)}>
更新
</button>
// ~

inputUpdate処理

inputにはstate(list)のtargetが表示されている。
inputUpdateではinputが変更されたタイミングで
state(list)のtargetを更新する
※targetは更新後の値を保持している

App.js
// 更新内容input入力時のstate更新
//リストから更新対象のオブジェクトのtargetプロパティのみを更新する
const inputUpdate = (event, i) => {
setList(
// mapで更新対象オブジェクトのみを変更した新規配列を作成し、stateにセットする
list.map((val, index) =>
//indexが一致していればtargetを上書き
index === i ? { ...val, target: event.target.value } : { ...val }
)
);
};

これでlistに格納されているオブジェクトは

todo
{
title:"更新前の値(画面に表示のみ)",
target:"更新後の値(inputに表示)"
}

の状態となる。

upd処理

更新ボタン押下でtodoを更新する。
処理内容としては更新対象のtitleをtargetで上書きする

App.jsx
// todolistの更新
const upd = (i) => {
setList(
// 更新内容inputを変更した時点でstateの配列に格納されているオブジェクトのtargetは
// 更新されているため、更新ボタン押下時は更新対象オブジェクトのterget→titleで更新する
// 新規配列をmapで作成し、stateにセットする
list.map((val, index) =>
//indexが一致した場合はtitleをtargetで上書きする
index === i ? { ...val, title: val.target } : { ...val }
)
);
};

削除の場合

stateのlistから対象のtodoを削除する。
stateのlistからfilterを使って削除対象以外のlistを再作成する

jsx部分

削除ボタン押下でdel処理を呼び出す。

App.jsx
// ~
{list.map((todoList, i) => (
<li key={i}>
<button className="button" onClick={() => del(i)}>
削除
</button>
// ~
</li>
// ~

削除

filterを使って、一致した要素を削除する

App.jsx
// todolistの削除
const del = (i) => {
// indexが一致したtodoは除去する
setList(list.filter((_, index) => index !== i));
};

まとめ

useStateでオブジェクト配列を扱う場合、スプレッド構文とmapやfilterなどを
を使う必要がある。

登録の場合

配列でスプレッド構文を使う

add処理
setList((prevList) => [
...prevList,
{ title: registInput.current.value, target: "" }
]);

新規配列内で変更前のlistをスプレッド構文で展開後、オブジェクトを新規追加する

更新の場合

mapとオブジェクトでスプレッド構文を使う

upd処理
const upd = (i) => {
setList(
list.map((val, index) =>
index === i ? { ...val, title: val.target } : { ...val }
)
);
};

mapを使って更新対象のオブジェクトをスプレッド構文を利用して更新(上書き)していく
オブジェクト内でスプレッド構文を使った場合、同じkeyがあれば後勝ちで上書きされる。
※今回はindexで更新しているが実際は、オブジェクト内にkeyを持たせた方がいい

削除の場合

filterを使う

del処理
const del = (i) => {
setList(list.filter((_, index) => index !== i));
};

filterで削除対象を省く

動作確認サンプル

実際に動作が確認できるソースをおいておく

React useState Array Sample

新着記事

タグ別一覧
top