はじめに
ReactのuseStateを使用した際の処理で引っかかった点があったのでまとめてみようと思います。
事象
以下のようなbuttonがクリックされたら配列の中身を表示する処理があります。
const [array, setArray] = useState([]);
const [element, setElement] = useState(0);
const onChangeElement = (event) => setElement(event.target.value);
const onClickButton = () => {
setElement(element);
const newArray = [...array, element];
setArray(newArray);
setElement(0);
console.log(array);
}
return (
<>
<div>
<input type={"number"} value={element} onChange={onChangeElement} />
</div>
<button onClick={onClickButton}>表示</button>
<p>={JSON.stringify(array)}</p>
</>
)
1,2,3と入力とそれぞれ入力していけば配列に値が追加されます。
ここで、console.log(array)
の部分はsetArray(newArray);
で新しい配列に置き換えているので、新しい配列がconsole.logに表示されると思っていました。
しかし、表示されたのは3を追加する前の古い配列でした。
以下のように実行しても、同じ結果です。
別の関数consoleLogsShow
でconsole.log(array);
を実行
const [array, setArray] = useState([]);
const [element, setElement] = useState(0);
const onChangeElement = (event) => setElement(event.target.value);
const onClickButton = () => {
setElement(element);
const newArray = [...array, element];
setArray(newArray);
setElement(0);
consoleLogsShow();
}
const consoleLogsShow = () => {
console.log(array);
}
原因
useStateは非同期に処理を行うため、onClickButton
の関数でconsole.log(array);
を行った段階では更新されていないようでした。
gptに聞いてみたら
これは React の状態管理に関する一般的な問題です。console.log の値が変更する前のものになっている理由は、React の useState フックが非同期で動作するためです。setArray や setElement を呼び出しても、その後すぐに console.log で状態を確認すると、まだ更新されていない古い値が表示されることがあります。
ということでした。
追記:上記の情報は間違いみたいでした。
setXX 関数がこの挙動の原因ではないようです。
js の関数のスコープの仕組みを使っているので、onClickButton の内側では、array 変数の中身はタイミングによらず必ず古い値となるみたいです。
コメントくださった方ありがとうございます!
以下、参考
解決策
- 新しい配列のnewArrayを使うようにする
これだと古い情報は表示されなくなりますね。
const onClickButton = () => {
setElement(element);
const newArray = [...array, element];
setArray(newArray);
setElement(0);
console.log(newArray);
}
- useEffectを使用する
arrayが変更されたら処理を実行してくれます。
const onClickButton = () => {
setElement(element);
const newArray = [...array, element];
setArray(newArray);
setElement(0);
}
useEffect( () => {
console.log(array)
}, [array])
終わりに
関数内でuseStateの値を使い、別の処理を実行したい場合は注意していきたいと思います。