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

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

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



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

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

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

 

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

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

    React會將多個setState的調(diào)用合并為一個來執(zhí)行,也就是說,當(dāng)執(zhí)行setState的時候,state中的數(shù)據(jù)并不會馬上更新

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

但是往往在實際的開發(fā)工作中,我們可能需要同步的獲取到更新之后的數(shù)據(jù),那么怎么獲取呢?下面介紹幾種常用的方法:
回調(diào)函數(shù)

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

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



這個時候大家可以看到控制臺打印的數(shù)據(jù)就是最新的了,我們也就實時的獲取到了最新的數(shù)據(jù)。
setTimeout

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

說再多文字不如代碼實踐,實踐才是檢驗真理的唯一標(biāo)準(zhǔn),下面我們還是以之前的例子為基礎(chǔ)改造一下代碼:

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



可以看見此時控制臺打印的數(shù)據(jù)是最新的數(shù)據(jù)。這也完美的印證了我們的猜想是正確的。
原生事件中修改狀態(tài)

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

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



經(jīng)過實踐,同樣這種方法也是可行的。

總結(jié):

    setState本身并不是異步,只是因為react的性能優(yōu)化機(jī)制體現(xiàn)為異步。在react的生命周期函數(shù)或者作用域下為異步,在原生的環(huán)境下為同步。
 


歡迎關(guān)注微信公眾號:猴哥說前端