react的setState到底是同步還是異步?


在介紹這個問題之前,我們先來看一下一個例子:

state = {
    number:1
};
componentDidMount(){
    this.setState({number:3})
    console.log(this.state.number)
}

看完這個例子,也許很多小伙伴會下意識的以為setState是一個異步方法,但是其實setState並沒有異步的說法,之所以會有一種異步方法的表現形式,歸根結底還是因為react框架本身的性能機制所導致的。因為每次調用setState都會觸發更新,異步操作是為了提高性能,將多個狀態合並一起更新,減少re-render調用。

試想一下如果在組件中有以下這樣一段代碼執行:

for ( let i = 0; i < 100; i++ ) {
    this.setState( { num: this.state.num + 1 } );
}

如果setState是一個同步執行的機制,那么這個組件會被重新渲染100次,這對性能是一個相當大的消耗。

顯然,React也是想到了這個問題,因此對setState做了一些特殊的優化:

❝ React會將多個setState的調用合並為一個來執行,也就是說,當執行setState的時候,state中的數據並不會馬上更新

這也很好的印證了剛才提到的那個例子。

但是往往在實際的開發工作中,我們可能需要同步的獲取到更新之后的數據,那么怎么獲取呢?下面介紹幾種常用的方法:

回調函數

setState提供了一個回調函數供開發者使用,在回調函數中,我們可以實時的獲取到更新之后的數據。還是以剛才的例子做示范:

state = {
    number:1
};
componentDidMount(){
    this.setState({number:3},()=>{
        console.log(this.state.number)
    })
}

這個時候大家可以看到控制台打印的數據就是最新的了,我們也就實時的獲取到了最新的數據。

setTimeout

上面我們講到了,setState本身並不是一個異步方法,其之所以會表現出一種異步的形式,是因為react框架本身的一個性能優化機制。那么基於這一點,如果我們能夠越過react的機制,是不是就可以令setState以同步的形式體現了呢?

說再多文字不如代碼實踐,實踐才是檢驗真理的唯一標准,下面我們還是以之前的例子為基礎改造一下代碼:

state = {
    number:1
};
componentDidMount(){
    setTimeout(()=>{
      this.setState({number:3})
      console.log(this.state.number)
    },0)
}

可以看見此時控制台打印的數據是最新的數據。這也完美的印證了我們的猜想是正確的。

原生事件中修改狀態

上面已經印證了避過react的機制,可以同步獲取到更新之后的數據,那么除了setTimeout以外,還有在原生事件中也是可以的。還是看一下例子:

state = {
    number:1
};
componentDidMount() {
    document.body.addEventListener('click', this.changeVal, false);
}
changeVal = () => {
    this.setState({
      number: 3
    })
    console.log(this.state.number)
}

經過實踐,同樣這種方法也是可行的。

總結:

❝ setState本身並不是異步,只是因為react的性能優化機制體現為異步。在react的生命周期函數或者作用域下為異步,在原生的環境下為同步。

 

  • setState 只在合成事件和鈎子函數中是“異步”的,在原生事件和 setTimeout 中都是同步的。

    • 合成事件:就是react 在組件中的onClick等都是屬於它自定義的合成事件

    • 原生事件:比如通過addeventListener添加的,dom中的原生事件

  • setState的“異步”並不是說內部由異步代碼實現,其實本身執行的過程和代碼都是同步的,只是合成事件和鈎子函數的調用順序在更新之前,導致在合成事件和鈎子函數中沒法立馬拿到更新后的值,形式了所謂的“異步”,當然可以通過第二個參數 setState(partialState, callback) 中的callback拿到更新后的結果。

  • setState 的批量更新優化也是建立在“異步”(合成事件、鈎子函數)之上的,在原生事件和setTimeout 中不會批量更新,在“異步”中如果對同一個值進行多次 setState , setState 的批量更新策略會對其進行覆蓋,取最后一次的執行,如果是同時 setState 多個不同的值,在更新時會對其進行合並批量更新。

state = { val: 0 }
 
 
 
batchUpdates = () => {
 
this.setState({ val: this.state.val + 1 })
 
this.setState({ val: this.state.val + 1 })
 
this.setState({ val: this.state.val + 1 })
 
}

 
hooks
const [val,setVal] = useState(0);
setVal(val=>val+1);
setVal(val=>val+1);
setVal(val=>val+1);
 
 
    • 其實val只是1,第二個是3

    • 具體大家可以看看react的源碼~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM