ReactでuseStateの値が更新されない仕様を理解する

2022/07/07に公開

はじめに

React で useState をよく使用すると思いますが、setState した後に State 値を確認したいということはよくあるかと思います。
しかし setState したのにも関わらず更新した値が確認できず、うまく動作しているか把握できないケースが発生しました。
私と同様にこの沼にハマってしまう方が多いのではないかと思いこの記事を書いてみます。
※これから React を勉強していこうという方に読んでいただければ幸いです。

1.state を更新したのにコンソールで更新された値が表示されない例

以下のコンポーネントで試してみようと思います

Sample.tsx
export const Sample: FC = () => {
  const [textValue, setTextValue] = useState("");
  const onChangeText = (e: ChangeEvent<HTMLInputElement>) => {
    setTextValue(e.target.value);
    console.log(textValue)
  };

  return (
    <div className="sample">
      <h2>テキスト入力</h2>
      <input type="text" onChange={onChangeText} value={textValue} />
    </div>
  );
}

テキストボックスに値を入力するたびに値がコンソールにはき出されるようになっています。

テキストボックスに「1」を入れてみます

テキストボックス1

コンソールには何も出力されてません。続いて「2」を入力してみます

テキストボックス2

「2」を入力したタイミングで「1」がコンソールに出力されました。
このことからsetTextValueした値は1つ遅れで更新されていることがわかります。

なぜこのようなことが起こっているかというと、

  • setTextValueは非同期で処理されておりコードの上から順に更新が行われていない
  • React の仕様で state は次のレンダリングされるタイミングまで反映されない

というようになっているからです。今回の場合、

「1」を入力

setTextValue

コンソール出力無

再レンダリング[textValue が更新される]

「2」を入力

setTextValue

コンソール出力「1」

となるわけです。因みに再レンダリングは state が更新されたタイミングで行われます。

2 更新する値の確認方法

state を更新直後に値を確認したい場合、setState に入れる値をそのままコンソール出力すれば良いです。

Sample.tsx
export const Sample: FC = () => {
  const [textValue, setTextValue] = useState("");
  const onChangeText = (e: ChangeEvent<HTMLInputElement>) => {
    setTextValue(e.target.value);
    console.log(e.target.value) // ここを更新値に変更
  };

  return (
    <div className="sample">
      <h2>テキスト入力</h2>
      <input type="text" onChange={onChangeText} value={textValue} />
    </div>
  );
}

このようなコードだとで更新したい値を直後に確認できます。

テキストボックスAfter1


setState で state 値はちゃんと更新されているので、ここは信用して先に進みましょう。。
(私はこれがちゃんと動いているか確認したかったですが、仕様上なので仕方ないですね)

Sample.tsx
export const Sample: FC = () => {
  const [textValue, setTextValue] = useState("");
  const onChangeText = (e: ChangeEvent<HTMLInputElement>) => {
    setTextValue(e.target.value);
    console.log(textValue)
  };

  return (
    <div className="sample">
      <h2>テキスト入力</h2>
      <input type="text" onChange={onChangeText} value={textValue} />
      <h2>{textValue}</h2>
    </div>
  );
}

テキストボックスOK1

まとめ

state の挙動を理解していないとここで沼にはまってしまい、他のコード箇所が間違っているのではないかと疑ってしまいます。
state はレンダリング後でしか反映されないということを念頭においてコードを記載していくのをおすすめします。

参考資料

https://mseeeen.msen.jp/react-setstate/
https://blog.shinki.net/posts/react-state-not-reflected

GitHubで編集を提案

Discussion