useStateの配列を更新しても再描画/表示されないときの解決法

投稿日:2022/10/27 最終更新日:2022/10/27

useStateの配列を更新しても再描画/表示されないときの解決法

React/Next.jsでよく起こる事象です。

簡単に解決出来ますので概念を説明しつつ解決法をご紹介します。

何が起こったか?

useStateで配列を更新した際に理想としては再描画されて更新後の内容が表示されていることを想定していました。

そのコードを直接貼れないため例を用意しました。

const [array, setArray] = useState([0, 1]);
const addNumber = (e: { target: { value: number } }) => {
 setArray(array.push(e.target.value));
};

return (
 <div>
  <input type="number" onChange={ inputNumber }>
  <button onClick={ addNumber }>追加する</button>
  <p>結果:{ array }</p>
 </div>
);

解説すると入力欄に数字を入れて「追加する」ボタンを押すと、入力した数字が配列に追加されて「結果:」の部分に配列の内容が表示されます。

この場合はクリックしたときに再描画がされて、「結果:」の部分が更新後の配列になると考えていましたが、クリックしても更新がされませんでした。

ただし、stateの中身は変更されているため、問題なのは再描画されていないということです。

解決法

以下の形へ変更します。

const [array, setArray] = useState([0, 1]);
const addNumber = (e: { target: { value: number } }) => {
 setArray([...array, e.target.value]); // ココ!
};

return (
 <div>
  <input type="number" onChange={ inputNumber }>
  <button onClick={ addNumber }>追加する</button>
  <p>結果:{ array }</p>
 </div>
);

実はuseStateを使用する際の注意点があります。

Reactではstateの値が変化した時にコンポーネントが再描画されます。
stateの値の変化を、Object.is() で判定しているとのことです。
なので、pushやspliceでは前回と同じ値と判定されるそうです。

Object.is() メソッドでは 2 つの値が同一値であるかどうかを判定します。

つまり、変更前と変更後では変化が起こらないということです。

なので、今回の追加するボタンで更新したarrayは中身の変更があっても、Object.is() メソッドでは変更がないと判断されてしまったわけです。

その際に使える方法が新たな配列をStateに渡すということです。

setArray([…array, e.target.value]); では現在の配列をスプレッド構文で展開し、最後尾に追加した数字を入れています。

この場合、[…array, e.target.value]は更新後の数字を含めた新たな配列として作成されます。

こうすることで、Object.is() メソッドは更新されたと判断されて再描画起こる流れです。

まとめると、「配列をuseStateで更新するときはスプレッド構文を使用する」と覚えておきましょう。