😥

勘違いしていたuseStateの挙動

2022/09/30に公開

こんにちハ! こんばんハ!
フロントエンドエンジニアとして働いているあんころもちたろうです。

Reactを独学で色々勉強していましたが、たまに躓いたりしました。。。
その躓いた内容を備忘録兼ねて書いていきたいと思います!

stateを更新したのに反映されない・・・

以下のようにstateを更新した後、更新したstateを使いたい時にアレ・・・更新されてない!
ということがありました。

import { useState } from 'react';

const Count = () => {
  const [ count, setCount ] = useState(0);
  const clickHandler = () => {
    setCount(count + 1);
    console.log(count);
  };

  return (
    <>
      <p>カウント: {count}</p>
      <button type="button" onClick={clickHandler}>countup</button>
    </>
  );
};

countの初期値を0にして、ボタンを押したときにclickHandler関数が実行され、
setCount関数でcountの値をcount + 1にしてます。
当然コンソールに表示されるcountの値は、最初は1になると思っていたら・・・!

え、0になっているなんで・・・。

でも<p>カウント: {count}</p>の箇所は正しく、カウント: 1となっているし、動作はしているはず。。。

というように私自身陥りました。
まぁそういうものかーと流していたのですが、理解したので備忘録として残したいと思います!

useStateの必要性

そもそもuseStateを使用しないで、記述した場合はどのようになるか見たいと思います。

const Count = () => {
  let count = 0;
  const clickHandler = () => {
    count = count + 1; // count++と同等
    console.log(count);
  };

  return (
    <>
      <p>カウント: {count}</p>
      <button type="button" onClick={clickHandler}>countup</button>
    </>
  );
};

上記の期待値としては、カウント: 1カウント: 2になることですが、通常の変数の場合だと値が更新されません。
コンソールでは期待していた値が来てます。

内部的にはcount変数が更新されているのですが、レンダリングされていません。
そこで先ほどのuseStateを使ってみます。
今度はCountコンポーネントがレンダリングされるタイミングを確認します。

import { useState } from 'react';

const Count = () => {
  const [ count, setCount ] = useState(0);
  console.log(`レンダリングされました。count: ${count}`);
  const clickHandler = () => {
    setCount(count + 1);
  };

  return (
    <>
      <p>カウント: {count}</p>
      <button type="button" onClick={clickHandler}>countup</button>
    </>
  );
};


※ strictモードだとコンソール上に2回出るらしいです。

countが更新される度にCountコンポーネントがレンダリングされているのが確認できると思います!
setCount関数が実行されると、再レンダリングするよう依頼します。
依頼するので直後に再レンダリングするのではなく、値の更新と再レンダリングは非同期で実行されます!

非同期で実行されるため、setCount実行した直後にconsoleでcountを確認しても値が更新されていないようです。

まとめ

useStateで実行する更新の関数(上記だとsetCount)は、非同期で値を更新・再レンダリングする。
これを忘れないようにしたいと思います!!

公式ドキュメントをちゃんと読んでいれば躓くことは無かったのかもしれない・・・。

Discussion