いくつかのコンポーネントでカウンターをインクリメントすることを想像してください:
class SomeComponent extends Component{
state = {
updatedByDiv: '',
updatedByBtn: '',
counter: 0
}
divCountHandler = () => {
this.setState({
updatedByDiv: 'Div',
counter: this.state.counter + 1
});
console.log('divCountHandler executed');
}
btnCountHandler = () => {
this.setState({
updatedByBtn: 'Button',
counter: this.state.counter + 1
});
console.log('btnCountHandler executed');
}
...
...
render(){
return (
...
// a parent div
<div onClick={this.divCountHandler}>
// a child button
<button onClick={this.btnCountHandler}>Increment Count</button>
</div>
...
)
}
}
親コンポーネントと子コンポーネントの両方にアタッチされたカウントハンドラがあります。これは意図的に行われるため、同じクリックイベントのバブリングコンテキスト内で、2つの異なるハンドラー内からsetState()を2回実行できます。
想像できるように、バブリングフェーズ中にイベントがターゲットから最も外側のコンテナにバブルするため、ボタンの1回のクリックイベントで両方のハンドラーがトリガーされます。
したがって、btnCountHandler()が最初に実行され、カウントが1にインクリメントされることが予想され、次にdivCountHandler()が実行され、カウントが2にインクリメントされることが予想されます。
ただし、React Developerツールで検査できるため、カウントは1までしか増加しません。
これは反応することを証明します
すべてのsetState呼び出しをキューに入れる
コンテキストの最後のメソッド(この場合はdivCountHandler)を実行した後、このキューに戻ります
同じコンテキスト内の複数のsetState呼び出し内で発生するすべてのオブジェクト変更をマージします(たとえば、単一のイベントフェーズ内のすべてのメソッド呼び出しは同じコンテキストです)、単一のオブジェクト変更構文にマージします(これは、状態プロパティを個別に更新できる理由ですそもそもsetState()で)
そして、それを1つのsetState()に渡して、複数のsetState()呼び出しによる再レンダリングを防止します(これはバッチ処理の非常に原始的な説明です)。
反応によって実行される結果のコード:
this.setState({
updatedByDiv: 'Div',
updatedByBtn: 'Button',
counter: this.state.counter + 1
})
この動作を停止するには、オブジェクトを引数としてsetStateメソッドに渡す代わりに、コールバックが渡されます。
divCountHandler = () => {
this.setState((prevState, props) => {
return {
updatedByDiv: 'Div',
counter: prevState.counter + 1
};
});
console.log('divCountHandler executed');
}
btnCountHandler = () => {
this.setState((prevState, props) => {
return {
updatedByBtn: 'Button',
counter: prevState.counter + 1
};
});
console.log('btnCountHandler executed');
}
最後のメソッドが実行を終了し、reactがsetStateキューを処理するために戻ると、キューに入れられた各setStateのコールバックを呼び出し、前のコンポーネントの状態を渡します。
このように反応することで、キュー内の最後のコールバックが、以前のすべての対応者が行った状態を更新できるようになります。