1.)setState操作是異步的,并且為了性能提升而進行批處理。這在setState的文檔中有解釋。
setState()不會立即改變this.state,但會建立挂起狀态轉換。調用此方法後通路this.state可能會傳回現有值。無法保證對setState的調用進行同步操作,并且可以對調用進行批處理以提高性能。
2.)為什麼它們會使setState異步,因為JS是單線程語言,并且此setState不是WebAPI或伺服器調用:
這是因為setState改變了狀态并導緻重新渲染。這可能是一項昂貴的操作,并使其同步可能會使浏覽器無響應。
是以,setState調用是異步的以及批處理以獲得更好的UI體驗和性能。
setState
意圖是異步的,在Dan Abramov的連結解釋中有一些非常好的理由。這并不意味着它總是異步的 - 它主要意味着你不能依賴它是同步的。ReactJS考慮了您正在更改狀态的場景中的許多變量,以确定何時
state
應該實際更新并重新呈現元件。
一個簡單的例子來說明,如果你打電話
setState
作為對使用者操作的反應,那麼
state
可能會立即更新(雖然,你再也不能指望它),是以使用者不會感到任何延遲,但如果你打電話
setState
為了響應ajax調用響應或其他一些不是由使用者觸發的事件,那麼狀态可能會稍微延遲更新,因為使用者不會真正感受到這種延遲,并且它會通過等待來提高性能批處理多個狀态一起更新并重新渲染DOM的次數更少
想象一下在某個元件中遞增計數器:
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>
...
)
}
}
有一個計數處理程式附加到父元件和子元件。這是故意完成的,是以我們可以在相同的單擊事件冒泡上下文中執行兩次setState(),但是從2個不同的處理程式中執行。
正如我們想象的那樣,按鈕上的單擊事件現在将觸發這兩個處理程式,因為事件在冒泡階段從目标氣泡到最外面的容器。
是以,btnCountHandler()首先執行,期望将計數增加到1然後執行divCountHandler(),期望将計數增加到2。
但是,計數隻會增加到1,因為您可以在React Developer工具中進行檢查。
這證明了反應
- 排隊所有setState調用
- 在上下文中執行最後一個方法後回到此隊列(在本例中為divCountHandler)
- 将在同一上下文中的多個setState調用内發生的所有對象變換(例如,單個事件階段中的所有方法調用都是相同的上下文)合并為一個單個對象變異文法(合并是有意義的,因為這就是我們可以獨立更新狀态屬性的原因在setState()中首先)
- 并将其傳遞給一個單獨的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');
}