useStateのsetState()は非同期

はじめに

Reactを使用している方なら以下のようなコードは数え切れないほどに書いていることでしょう。

const [state, setState] = useState(0);

今回は、useState()から返されるステート更新用関数は非同期で実行される、という点について書いていきます。  

実験

以下のコードで試してみましょう。

  const [count, setCount] = useState(0);

  const onClick = () => { 
    setCount(count + 1);
    console.log(count); // 最初にクリックしたときは「0」が出力される
  }

  return <div>
    <div>{count}</div>
    <button onClick={onClick}>increment!</button>
  </div>

上記の例を見ても分かる通り、setCount(count + 1)が実行されても、直後にcountが更新されていないことがわかります。

さらに、例えば以下のようにdoubleCountUpがあるとしましょう。

  const doubleCountUp = () => {
    setCount(count + 1);
    setCount(count + 1);
  }

上記のdoubleCountUpが実行されたとしても、再レンダー後にはcountは1しか増えません。

解決案

以下のように修正することで、再レンダー後に期待した結果が得られるようになります。

  const doubleCountUp = () => {
    setCount(previous => previous + 1);
    setCount(previous => previous + 1);
  }

なぜ

なぜsetStateが非同期に実行されるかを説明しておきます。
仮に同期的に実行されるとすると、setState()の呼び出しごとにコンポーネントの再レンダーが発生してしまい、パフォーマンスが非常に悪くなってしまいます。   そのため、ReactsetStateをまとめて処理することで再レンダーを最小限に留めています。  

参考

What’s with functions inside setState? | by nashe omirro | Oct, 2021 | Medium