ReactのComponent.setStateによるstateの書き換えは非同期処理である
Reactはパフォーマンス最適化のために、setStateが呼ばれたからと言って、実際のstateを即書き換えるわけではありません。
stateの更新はReact内部でよろしくまとめられて非同期で行われます。
例えば、以下のコードはうまく動いてくれません。
// this.state.hoge = 'fuga'の状態とします。
this.setState({
hoge:'piyo';
});
console.log(this.state.hoge);//出力されるのはpiyoではなくfuga
困りそうなケースと対応策
state変更後、後続の処理でstateの値を参照して処理したい
this.setState({
hoge:'piyo';
});
fuga();//fugaの中でthis.state.hogeが参照される
気を付けていないと意外と書いてしまうかも知れません。
対応策
同期処理が必要な場合はstate経由は不適切なので、素直にfugaの引数として渡しましょう。
インクリメントなど前のstateに依存する処理
this.setState({
hoge:this.state.hoge + 1;
});
こんな感じの元のstateの値に依存する処理の場合、
連続で実行されると想定通りに動いてくれません。
例えば、 this.state.hoge=3
の状態で連続して上記の処理を2回実行すると、
一回目は hoge: 3 + 1
と想定通りに動いてくれますが、
二回目までの間にthis.state.hogeが4に切り替わらないので、
二回目もhoge: 3 + 1
をsetStateの引数として渡してしまいます。
こうなるとReactは「 hoge:4
が2回来たけど一回でいいな。」と考えてしまうので
hogeは5になりません。
対応策
functionをsetStateの引数として渡しましょう。
this.setState((state,prop) => {
return {hoge:state.hoge + 1};
});
このようにstateとpropを引数に取ると、stateが切り替わってから関数を実行してくれるので、
前のstateに依存する処理を書いても正しくstateを変更できます。
stateの変更によってComponentが再描画されてから処理したい場合
setStateの第二引数にコールバック関数を渡しましょう。
まとめ
- setStateしてもthis.stateはすぐには書き換わらない
- 同期処理をしたい場合は素直に引数で
- インクリメント系処理は関数をsetStateに渡す
- 描画処理まで一通り終わってからの場合はコールバック関数