【React】useStateの更新って、めっちゃ遅い

何をしているのか?

ReactでTodoリストを作成中。タスク一覧の表示されるタスクのステータスボタンを押すと、「作業中」→「完了」、「完了」→「作業中」に切り替わる。

今回の課題

タスクのステータスボタンをクリックする度に、ブラウザの表示上ではステータスがちゃんと切り替わっている。

よしよし、ええ感じやん。
念の為、コンソールでステータスを切り替えたタスクのオブジェクトを表示して、ちゃんとステータスが切り替わっているかを確認しよう。

const handleTodoChangeStatus = (id, todoCurrentStatus) => {
  setTodoItems((prevTodoItem) => {
    return prevTodoItem.map((todoItem) => {
      return todoItem.id === id ? { ...todoItem, todoStatus: todoCurrentStatus === NOT_START ? DONE : NOT_START } : todoItem;
    })}
    )
  console.log('ステータスを切り替えたタスク:',todoItems);
}

もう一回、ステータスボタンをクリックしてっと…
…あれ? ステータス(todoStatus)の値が「作業前」→「完了」に切り替わらない…

なんでや、もう一回、クリックしたろ。

なんでや、今度はちゃんと切り替わった。
これ、ちゃんとステータスが更新れているのか?

今回の学び

感覚値として、useStateで管理している値の更新はめっちゃ遅い。
すぐに反映されない。
もっと詳しく調べると、useStateの値の更新と再レンダリングは非同期で処理されるため、stateを更新した直後には値が反映されないのとこと。

今回、コンソールでステータス(todoStatus)の値を確認するため、console.log()を関数に仕込んだ。

この時、useStateの値を更新する処理と、console.log()でタスクの情報をコンソールに出力する処理、2つの処理が同時にスタート。useStateの値の更新は遅い(非同期で処理される)ので、値が更新される前のタスクの情報がコンソールに出力された。だから、ステータスが更新されていないように見えた。

試しに、以下のように、console.log()を2箇所に仕込んでみる。

const handleTodoChangeStatus = (id, todoCurrentStatus) => {
  setTodoItems((prevTodoItem) => {
    return prevTodoItem.map((todoItem) => {
      return todoItem.id === id ? { ...todoItem, todoStatus: todoCurrentStatus === NOT_START ? DONE : NOT_START } : todoItem;
    })}
    )
  console.log('A:',todoItems);
}

  console.log('B:',todoItems);

【A】関数が実行される度に、タスクの情報をコンソールに出力。
【B】App.jsがレンダリングされる度に、タスクの情報をコンソールに出力。

同じように、ステータスボタンを押してタスクのステータスを切り替えると、以下のような内容がコンソールに出力される。

handleTodoChangeStatus関数を実行したとき、最初に【A】のコンソールが実行され、todoStatusの値が更新される。その後、stateが更新されたことによってレンダリングが起こり、【B】のコンソールが実行される。その時にはすでにtodoStatsuの値は更新されているので、コンソールには値が更新されたオブジェクトの情報が出力される。

まとめ

  • useStateは非同期処理で値を更新している。

  • 分かりやすくいうと、useStateの値の更新はめっちゃ遅い。

  • コンソールに出力する処理の方が早いので、更新されていないように見えるが、ちゃんと更新はされている。

  • 値の更新を確認するために、コンソールを仕込むのは良いが、仕込む場所を間違えないように注意する。useStateの値を更新する関数と同じ場所には仕込まない。


記事を書くスタイルに迷走している感がすごい。
どのように書くのが一番負担が少ないかなぁ…。

この記事が気に入ったらサポートをしてみませんか?