LoginSignup
23
13

More than 5 years have passed since last update.

[React]setState は state の変更リクエストを出すだけ

Last updated at Posted at 2019-04-26

概要

機会があって React をいじっていたときのことです。

setState() したのに state が更新されていない!なぜ!?ホワイ!?」

という出来事がありました。そのときの失敗談を共有したいと思います。

詳細

サンプルコードです。
注目してほしいのは componentDidMount のところです。setState を呼んだ直後に state をコンソールに出してみたところ値が変わっていません。(この時点で、「ん?それの何がおかしいの?」 という賢明な識者の方は特にこの記事から得るものは何もないと思いますのでブラウザバックでおkです)

class MessageBox extends React.Component {
  constructor() {
    super()
    console.log("Called constructor")

    //初期値
    this.state = {
      message: "なんでもしますから"
    }
  }

  componentDidMount() {
    console.log("Called componentDidMount")

    //state を更新(した気になる...がしかし)
    this.setState({message: "なんでもするとは言ってない"})

    console.log("Called setState in componentDidMount")

    //state が変わってない!!
    console.log("state.message => " + this.state.message) // => "なんでもしますから"
  }

  render() {
    console.log("Called render")

    //ここでようやく setState で予約した state の変更を確認
    console.log("state.message => " + this.state.message) //=> "なんでもするとは言ってない"
    return (<p>message: {this.state.message}</p>)
  }
}

React.render(<MessageBox />, document.getElementById('app'));

componentDidMountsetState を呼ぶという状況があったんですが、その直後に state から値を取得したら古いもののままでした。componentDidMount 内ですぐ更新後の値を使いたい、という場面があったためちょっとハマりました。

答え

公式ドキュメントに載ってました
https://reactjs.org/docs/react-component.html#setstate

Think of setState() as a request rather than an immediate command to update the component.

setState() は即コンポーネントの更新をするコマンドではない ことがはっきり書かれています。setState() を呼んだ段階では state の変更リクエストを出したにすぎず、その時点で即時反映されるわけではないんですね。 仕様通りやん\(^o^)/ ここでは componentDidMount の処理がまだ続いているため再レンダリングは行われていません。なので state の変更リクエストはまだ処理されていないということなのでしょう。

言われてみればそうしないと、componentDidMount 内で setState を何度も呼んだ場合は、その度に再レンダリングが行われてしまいそうです。なるほど、そう考えると理に適ってます。でも名前がちょっと紛らわしいのでいっそのこと setChangeStateRequest() みたいな名前でもよかったようなw

console.log での確認

console.log を節目に仕込んでみましたので出力からも挙動を確認できます。
このサンプルコードを実行すると以下のような状態になります。(見やすくするため、メソッドごとに改行は入れてます)

"Called constructor"
"Called render"
"state.message => なんでもしますから"

"Called componentDidMount"
"Called setState in componentDidMount"
"state.message => なんでもしますから" //まだ setState する前と値が変わってない!!

"Called render"
"state.message => なんでもするとは言ってない" //ここでようやく setState が反映される

今日の教訓

setState() しても state の更新を予約するだけで、すぐには state は更新されません。render() が呼ばれる前に更新後の値をすぐ使いたい、という場合はご注意ください。(あまりないとはおもいますが)

おまけ

検証に使ったコードを CodePen に残してみました。興味のある方はお立ち寄りください。


See the Pen
React at CodePen
by TOMMY a.k.a. Jpsern, Jps. (@jpsern)
on CodePen.


23
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
13