🍇

【React】「useStateの値を更新しても反映されない!」の解決方法

2020/12/18に公開

ReactのuseStateを使ってセットした値が更新されていないということが起こったので、ここにまとめておきます。

useStateとは?

useStateは状態変数と状態変数を更新する関数を返す。状態変数が更新されるとコンポーネントを再レンダリングし、以下の記述で定義できます。

const [状態変数, 状態変数の更新関数] = useState(初期値)

useStateでセットした値で更新されていない状況

以下のソースではクリックしたときにcountを1ずつ増やしています。

import {useState} from "react"

export default function Index () {
    const [count, setCount] = useState(0);
    const onButtonClick = () => {
      setCount(count + 1);
      alert(`count:${count}`);
    }

  return (
    <div>
      <button onClick={onButtonClick}>押して!</button>
    </div>
  )
}

この画面でボタンを押したときに、表示されるメッセージは何でしょうか?

答えは「count:0」です。一見すると、onButtonClickcountが0の状態から+1した値をセットしているので「count:1」が表示されそうですが、、、

なぜセットした値が更新されていないのか?

なぜセットした値が更新されていないのかというと、setStateで値が更新されるのは関数が呼び出された後だからです。

レンダリングされるたびにコンポーネント関数を呼び出しており、countは関数ごとに保持しています。

//初回レンダリング
function Index() {
    const count = 0;
    // ...
}

//クリック後の関数コンポーネント
function Index() {
    const count = 1;
    // ...
}

//2回クリック後の関数コンポーネント
function Index() {
    const count = 2;
    // ...
}

そのため、同じ関数内でsetCountで新しい値に更新してもcountはまだ古い値のままなのです。

更新した値で表示するには?

では、更新した値で値を表示するにはどうすればいいのでしょうか?

方法は2つあります

  • 新しい値を変数で保持する
  • 関数型の更新を使う

1.新しい値を変数で保持する

1つ目が新しい値を変数で保持する方法です。new_count変数を定義して新しい値を保持しています。

import {useState} from "react"

export default function Index () {
  const [count, setCount] = useState(0);
  const onButtonClick = () => {
    const new_count = count + 1;
    setCount(new_count);
    alert(`count:${new_count}`);
  }

  return (
    <div>
      <button onClick={onButtonClick}>押して!</button>
    </div>
  )
}

2.関数型の更新を使う

2つ目が関数型の更新を使う方法です。setCountメソッドに関数を渡すと第1引数に前回の値が入っているので、それを利用します。引数には常に最新の値が取得できるため、そのままの値を表示すれば同じ動きにできます。

import {useState} from "react"

export default function Index () {
  const [count, setCount] = useState(0);
  const onButtonClick = () => {
    setCount((pre_count) => pre_count + 1)  //初回クリック時のpre_countは0
    setCount((pre_count) => {               //初回クリック時のpre_countは1
      alert(`count:${pre_count}`);
      return pre_count
    })
  }

  return (
    <div>
      <button onClick={onButtonClick}>押して!</button>
    </div>
  )
}

まとめ

  • useStateは状態と状態を更新する関数を返す
  • useStateのset関数はレンダリング後に更新される
  • setterに関数を渡すと引数に最新の値が取得できる

参考

https://overreacted.io/ja/a-complete-guide-to-useeffect/
https://ja.reactjs.org/docs/hooks-reference.html
https://blanktar.jp/blog/2020/06/react-why-state-not-updated

Discussion