setState 是 React 用於管理狀態的一個特殊函式,我們在 React 中會經常使用到它,下面的場景你一定遇到過:
export class Todo extends React.Component{
...
increaseScore () {
this.setState({count : this.state.count + 1});
this.setState({count : this.state.count + 1});
}
...
}
複製程式碼
上面這段程式碼, increaseScore
函式中希望將 count 這個狀態的值在原來的基礎上加1再加1,但是實際情況其實並不想你預期的那樣,如果你在控制檯把count的值打出來,會發現它只增加了1!
為什麼呢?
setState 是非同步的
setState 是非同步的
看一下這個例子
class BadCounter extends React.Component{
constructor(props){
super(props);
this.state = {count : 0}
}
incrementCount=()=>{
this.setState({count : this.state.count + 1})
this.setState({count : this.state.count + 1})
}
render(){
return <div>
<button onClick={this.incrementCount}>+2</button>
<div>{this.state.count}</div>
</div>
}
}
class GoodCounter extends React.Component{
constructor(props){
super(props);
this.state = {count : 0}
}
incrementCount=()=>{
this.setState((prevState, props) => ({
count: prevState.count + 1
}));
this.setState((prevState, props) => ({
count: prevState.count + 1
}));
}
render(){
return <div>
<button onClick={this.incrementCount}>+2</button>
<div>{this.state.count}</div>
</div>
}
}
複製程式碼
在這個demo中,上下兩個計數器都是希望實現點選後數+2,但是實際效果如下,只有第二個計數器達到了我們的預期:
結合程式碼可以發現兩個計數器的區別給setState
傳遞的引數不一樣
// 在錯誤示例中
this.setState({count : this.state.count + 1})
this.setState({count : this.state.count + 1})
// 在正確示例中
this.setState((prevState, props) => ({
count: prevState.count + 1
}))
this.setState((prevState, props) => ({
count: prevState.count + 1
}))
複製程式碼
查閱資料發現,在多次呼叫setState()
時,React 並不會同步處理這些setState()
函式,而是做了一個“批處理”——如果使用物件作為引數傳遞給setState
,React 會合並這些物件。
而同樣的情況下,當你給setState()
傳入一個函式時,這些函式將被放進一個佇列,然後按呼叫順序執行。
這裡是react的核心成員 Dan Abramov 對 setState 為什麼是非同步的解釋
總結
連續多次執行 setState 操作的情況是很常見的,下一次你如果在 setState 時需要用到 this.state 的時候要切記,在 setState 中利用函式操作來取得上一次的 state 值才是正確的做法!