先日、useStateを使ってとあるstateを更新しようとしたところ、全く意図した挙動にならなかった。
最初はuseStateが原因ではなく他のコードが悪さをしていると思い、書いたコードを片っ端から確認したが結局原因分からず。
ふと、useStateが原因ではないかと思い至って、いじったところ見事エラーが解消しました。
今後同じようなエラーでハマりたくないので、今回は戒めてとして記事を書こうと思います。
※因みに実際に発生したエラーのコードは複雑になっているので、別例を使って今回のエラーを振り返りたいと思います。
どのようなエラーでハマったのか
まずはハマったエラーを簡素的に表そうと思ったのですが、難しかったので一般的に陥りそうな例をあげてみます。
const [currentNum, setCurrentNum] = useState(0)
const onHandleClick = () => {
setCurrentNum(currentNum + 1);
setCurrentNum(currentNum + 1);
}
イマージ的には上記のような処理をしようとしていたと思ってください。
この時に私の中ではonHandleClickを呼び出したらcurrentNumが2づつ増えていくものだと思っていました。
しかし実際にはonHandleClickを呼び出しても+1にしかなりません。
これはなぜなのか?
一連の処理の流れ
上記で挙げたコードではどうしてcurrentNumが+2にならずに、+1なのか。
それは、この時のcurrentNumには初期値の0が入ってしまっているからです。
具体的にコードで表すと以下のようになっています。
const [currentNum, setCurrentNum] = useState(0)
const onHandleClick = () => {
setCurrentNum(0+ 1)
setCurrentNum(0+ 1)
//両方の更新関数には初期値の0が入ってしまっているので、
//このonHandleClick関数内でいくら更新関数を継ぎ足しても1にしかならない
//因みにcurrentNumに1が入っていれば2にしかなりません。0というのはあくまでも一例です。
}
ではどのようにすれば意図したように+2にづつ増やす事ができるのか?
解決方法
答えは更新関数で値が更新されるのは関数が呼び出された後なので、
更新関数を実行する際に前回の値を渡してあげることです。
こちらも具体的にコードで表すと以下になります。
const [currentNum, setCurrentNum] = useState(0)
const onHandleClick = () => {
setCurrentNum((currentNum) => currentNum + 1)
setCurrentNum((currentNum) => currentNum + 1)
//関数で表すことで,コンポーネントのレンダリング走らなくとも引数に前の状態が入って、そこに+1を行っている形になります。
//なので最初の更新関数の引数には初期値の0が入っている状態ですが、
//2回目の更新関数の引数には最初の更新関数で更新された1が入っています。
最後に
ふとした瞬間にまた忘れて同じ過ちを繰りかえさないようしにしていきたいものです。