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)注微信公眾號:猴哥說前端