useStateの注意点(状態の変化のタイミング,関数コンポーネントの中の非同期関数と状態の関係)

2023/03/08に公開

コンポーネントの中では、再レンダリングされるまで、状態は変化しない

直感的には、setNumber 関数が3回呼び出されると number が 3 になりそうであるが、onClick を実行しても number は 1 のままである。
Counter コンポーネントが再レンダリングするときに、状態が変更されます、

import { useState } from "react";

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button
        onClick={() => {
          setNumber(number + 1);
          setNumber(number + 1);
          setNumber(number + 1);
        }}
      >
        +3
      </button>
    </>
  );
}

状態は非同期関数による影響は受けません

onClick を実行すると、

  1. setNumber 関数が実行され、React に状態を保存し、再レンダリングが予約されます。
  2. 非同期関数(setTimeout)を実行する。
  3. 再レンダリングが走り、状態が更新されて,画面に再レンダリング後の値が表示される
  4. setTimeout の中の関数が実行される

この時、再レンダリング前の状態を取得するので、画面に出力される alert のダイアログは再レンダリング前の状態になります。
イベント ハンドラーのコードが非同期であっても、状態変数の値がレンダリング内で変更されることはありません。

import { useState } from "react";

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button
        onClick={() => {
          setNumber(number + 5);
          setTimeout(() => {
            alert(number);
          }, 3000);
        }}
      >
        +5
      </button>
    </>
  );
}
GitHubで編集を提案

Discussion