好久沒寫東西了淮蜈。。已卷。梧田。
來(lái)頭條之后, 每天身體被掏空侧蘸。也沒多少時(shí)間寫東西裁眯,
今天周末,上午沒啥事讳癌, 簡(jiǎn)單寫寫穿稳,
一個(gè)例子
假如有這樣一個(gè)點(diǎn)擊執(zhí)行累加場(chǎng)景:
// ...
this.state = {
count: 0,
}
incrementCount() {
this.setState({count: this.state.count + 1});
}
handleIncrement = () => {
this.incrementCount();
this.incrementCount();
this.incrementCount();
}
<button ref="button" onClick={this.handleIncrement}>點(diǎn)擊</button>
// ...
每一次點(diǎn)擊晌坤, 會(huì)執(zhí)行三次累加逢艘,看一下輸入:
并沒有達(dá)到預(yù)期的效果旦袋,糾正也很簡(jiǎn)單:
incrementCount() {
this.setState((prevState) => {
return {count: prevState.count + 1}
});
}
再看輸出:
setState 的時(shí)候, 一個(gè)傳入了object, 一個(gè)傳入了更新函數(shù)埋虹。
區(qū)別在于: 傳入一個(gè)更新函數(shù)猜憎,就可以訪問(wèn)當(dāng)前狀態(tài)值娩怎。 setState調(diào)用是批量處理的搔课,因此可以讓更新建立在彼此之上,避免沖突截亦。
那問(wèn)題來(lái)了爬泥, 為什么前一種方式就不行呢? 具體一點(diǎn)說(shuō)崩瓤, setState為什么不會(huì)同步更新組件狀態(tài)呢袍啡? 帶著這個(gè)疑問(wèn),繼續(xù)往下看却桶。
進(jìn)入這個(gè)問(wèn)題之前境输,我們先回顧一下現(xiàn)在對(duì)setState的認(rèn)知:
- setState不會(huì)立刻改變React組件中state的值.
- setState通過(guò)觸發(fā)一次組件的更新來(lái)引發(fā)重繪.
重繪指的就是引起React的更新生命周期函數(shù)4個(gè)函數(shù):
- shouldComponentUpdate(被調(diào)用時(shí)this.state沒有更新;如果返回了false颖系,生命周期被中斷嗅剖,雖然不調(diào)用之后的函數(shù)了,但是state仍然會(huì)被更新)
- componentWillUpdate(被調(diào)用時(shí)this.state沒有更新)
- render(被調(diào)用時(shí)this.state得到更新)
- componentDidUpdate
- 多次setState函數(shù)調(diào)用產(chǎn)生的效果會(huì)合并嘁扼。
如果每一次setState調(diào)用都走一圈生命周期,光是想一想也會(huì)覺得會(huì)帶來(lái)性能的問(wèn)題,其實(shí)這四個(gè)函數(shù)都是純函數(shù)球散,性能應(yīng)該還好欣鳖,但是render函數(shù)返回的結(jié)果會(huì)拿去做Virtual DOM比較和更新DOM樹,這個(gè)就比較費(fèi)時(shí)間不傅。
目前React會(huì)將setState的效果放在隊(duì)列中旅掂,積攢著一次引發(fā)更新過(guò)程,為的就是把Virtual DOM和DOM樹操作降到最小访娶,用于提高性能商虐。
查閱一些資料后發(fā)現(xiàn),某些操作還是可以同步更新this.state的震肮。
直接說(shuō)結(jié)論吧:
在React中称龙,如果是由React引發(fā)的事件處理(比如通過(guò)onClick引發(fā)的事件處理),調(diào)用setState不會(huì)同步更新this.state戳晌,除此之外的setState調(diào)用會(huì)同步執(zhí)行this.state鲫尊。
所謂“除此之外”,指的是繞過(guò)React通過(guò)addEventListener
直接添加的事件處理函數(shù)沦偎,還有通過(guò)setTimeout/setInterval
產(chǎn)生的異步調(diào)用疫向。
https://jsbin.com/mavolejufi/edit?html,js,console,output
盜用一張圖:
在React的setState函數(shù)實(shí)現(xiàn)中咳蔚,會(huì)根據(jù)一個(gè)變量isBatchingUpdates判斷是直接更新this.state還是放到隊(duì)列中。
而isBatchingUpdates默認(rèn)是false搔驼,也就表示setState會(huì)同步更新this.state谈火,但是有一個(gè)函數(shù)batchedUpdates,這個(gè)函數(shù)會(huì)把isBatchingUpdates修改為true舌涨,而當(dāng)React在調(diào)用事件處理函數(shù)之前就會(huì)調(diào)用這個(gè)batchedUpdates糯耍,造成的后果,就是由React控制的事件處理過(guò)程setState不會(huì)同步更新this.state囊嘉。
知道上面的一些理論之后温技, 我們?cè)倏匆粋€(gè)例子就很清晰了:
class Example extends React.Component {
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 1 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 2 次 log
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 3 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 4 次 log
}, 0);
}
render() {
return null;
}
};
毫無(wú)疑問(wèn)輸出 0 0 2 3 ;
前兩次在isBatchingUpdates 中扭粱,不用更新state, 輸出兩個(gè)0舵鳞。
后面兩次會(huì)同步更新, 分別輸出2琢蛤, 3蜓堕;
上面的例子,我們就知道了setState 是可以同步更新的博其,但是還是盡量避免直接使用套才, 僅作了解用。
如果你非要玩一些騷操作贺奠,寫出這樣的代碼去直接去操作this.state:
this.state.count = this.state.count + 1;
this.state.count = this.state.count + 1;
this.state.count = this.state.count + 1;
this.setState();
我只能說(shuō)霜旧, 大胸弟, 你很騷儡率。吾有舊友叼似汝挂据,而今墳草丈許高。
根據(jù)以上內(nèi)容儿普, 簡(jiǎn)單重復(fù)下結(jié)論:
- 不要直接去操作this.state崎逃, 這樣會(huì)造成不必要的性能更問(wèn)題和隱患。
- 由React引發(fā)的事件處理眉孩,調(diào)用setState不會(huì)同步更新this.state个绍,除此之外的setState調(diào)用會(huì)同步執(zhí)行this.state。
就簡(jiǎn)單寫到這吧浪汪, 要出門了 :)巴柿。
擴(kuò)展閱讀:
https://reactjs.org/docs/faq-state.html#what-does-setstate-do
關(guān)于事務(wù),還有相關(guān)源碼的問(wèn)題可以參考:
https://zhuanlan.zhihu.com/p/20328570
https://zhuanlan.zhihu.com/p/25882602
https://zhuanlan.zhihu.com/p/26069727
https://zhuanlan.zhihu.com/p/25990883
https://reactjs.org/docs/faq-state.html#what-does-setstate-do
https://reactjs.org/docs/react-component.html#setstate