🍇

state変数に配列を持つ場合の更新方法

2023/05/22に公開

Reactのstateで保持しているオブジェクトを更新する際には、直接そのオブジェクトを変更するのではなく、新しいオブジェクトを作る、もしくは既存のオブジェクトのコピーを作って、setXXX(newObject)で更新する必要があります。

stateに配列を保持する場合も同様です。

const [list, setList] = useState([0]);

const ngHandler1 = () => {
  // NG
  list[0] = 10;
}

const ngHandler2 = () => {
  // NG
  list.push(10);
  setList(list);
}

const okHandler = () => {
  // OK
  const newList = f(list); // どうにかして新しいリストを作る
  setList(newList);
}

新しい配列を作る方法はいくつかありますが、公式ドキュメントにどのようにすれば良いかがまとまっていました。
以下に新しい配列を作る方法についてまとめます(ほぼ翻訳&要約です)。

詳しくはこちらの公式ドキュメントにまとまっています。
https://react.dev/learn/updating-arrays-in-state

要素を追加したいとき

スプレッド構文を利用します。

const [list, setList] = useState([0]);

const handler = () => {
  setList([...list, 10]);
}

上記では、元のリストの末尾に「10」という要素を追加した新しい配列を作り、stateを更新しています。
以下のようにすることで、リストの先頭に要素を追加することもできます。

const handler = () => {
  setList([10, ...list]);
}

要素を削除したいとき

filterを利用します。
削除したい要素を取り除き、新しい配列を作ります。

const [list, setList] = useState(["a", "b", "c"]);

const handler = () => {
  setList(list.filter(item => item !== "b"))
}

上記のようにすることで、bという要素を取り除くことができます。

要素を変更したいとき、置き換えたいとき

mapを利用します。

以下では一部の要素を変更します。

const [list, setList] = useState([
  {id: 0, type: "comic", count: 10},
  {id: 1, type: "magazine", count: 5}, 
  {id: 2, type: "novel", count: 22}, 
]);

const handler = () => {
  setList(list.map(item => {
    if(item.type === "comic") {
      return item;
    } else {
      return {
        ...item,
        count: item.count + 1
      }
    }
  }))
}

以下では一部の要素を置き換えます。

const [list, setList] = useState(["a", "b", "c"]);

const handler = () => {
  setList(list.map(item => {
    if(item !== "b") {
      return item.toUpperCase();
    } 
    return item;
  }))
}

要素を特定の位置に挿入したいとき

先頭でも末尾でもない位置に要素を挿入したい場合は、スプレッド構文とsliceを利用します。

以下では、acの間にbを挿入します。

const [list, setList] = useState(["a", "c"]);

const handler = () => {
  const insertAt = 1;
  setList([
    ...list.slice(0, insertAt),
    "b",
    ...list.slice(insertAt)
  ])
}

その他の変更方法

スプレッド構文やmapやfilterだけではできないこともいくつかあります。例えば、配列を反転したり並び替える操作です。
JavaScriptのreversesortは元の配列を直接変更するため、stateの更新には利用できません。
ただし、最初に配列をコピーして、コピーした配列に操作を行うことはできます。

以下では配列の中身を反転します。

const [list, setList] = useState(["a", "b", "c"]);

const handler = () => {
  const newList = [...list];
  newList.reverse();
  setList(newList)
}

Discussion