React setState流程解析
一、setState使用
接觸react框架不久,卻在項目當中發現,非受控組件其更新時機的觸發方式——setState,是一個異步的過程
下面是一個例子:
handelTabChange (tabName) {this.setState({tab: tabName})this.updateTabPane() }updateTabPane () {const { tab } = this.stateconsole.log( tab ) // not the latest one }此時tab發生變化的時候,輸出的卻依然是上一個tab的名稱,因此可以判斷updateTabPane是在setState之前執行了。
那么,為什么setState需要異步去改變組件的state呢?
React組件是靠單向數據流構建頁面dom的,除開props,自身的state改變也是引起組件渲染的主要因素,為了節省性能消耗,react有一套自己的state更新策略,為的是減少state更新對頁面渲染的消耗,而這套更新策略的入口就是setState方法。
這同樣也解釋了為什么直接對this.state進行賦值操作并不能改變頁面的渲染結果。
那么在異步的過程中,setState究竟做了哪些事兒呢?
二、 setState更新組件state
總體歸納一下setState更新組件的流程,調用setState后,會把我們想要更新的state壓進一個待更新隊列(即內部實例的_pendingStateQueue),然后執行入棧更新操作enqueueUpdate,判定是否處于批量更新狀態。如果正在進行組件的批量更新,那么久先把實例推進dirtyComponents里面等待下一次批量更新;相反若沒有批量更新在執行,則會調用一個批量更新的事務。
/*** setState源碼 **/ReactComponent.prototype.setState = function (partialState, callback) {this.updater.enqueueSetState(this, partialState); // 傳入新state進隊列if (callback) { // 推入callback隊列this.updater.enqueueCallback(this, callback, 'setState')} }enqueueSetState: function(publicInstance, partialState) {// 獲取內部實例var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState')if (!internalInstance) {return}// 更新隊列合并操作var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = [])queue.push(partialState)// 更新代碼enqueueUpdate(internalInstance) }setState把我們希望更新的partialState推入待更新隊列之后,就撒手交給enqueueUpdate去處理更新的時機了,我們看一下enqueueUpdate又為我們做了什么
/*** enqueueUpdate源碼 **/function enqueueUpdate(component) {ensureInjected()// 如果不處于批量更新模式if (!batchingStrategy.isBatchingUpdates) {batchingStrategy.batchedUpdates(enqueueUpdate, component)return}// 如果處于批量更新模式,則將該組件保存在 dirtyComponents 中dirtyComponents.push(component) }可以看到enqueueUpdate當中出現了一個重要的對象batchingStrategy,他有一個屬性isBatchingUpdates用來告訴enqueueUpdate是應該更新,還是應該等待,把組件推入dirtyComponents里。可以想象這是一個react內部,用于控制批量更新的對象,讓我們更近距離的了解它
/*** batchingStrategy源碼 **/var ReactDefaultBatchingStrategy = {isBatchingUpdates: false,batchedUpdate: function(callback, a, b, c, d, e) {var alreadyBatchingStrategy = ReactDefaultBatchingStrategy. isBatchingUpdatesReactDefaultBatchingStrategy. isBatchingUpdates = trueif (alreadyBatchingStrategy) {callback(a, b, c, d, e)} else {transaction.perform(callback, null, a, b, c, d, e)}} }dirtyComponents當中提供的batchedUpdates其實就是我們一直尋找的,真實用來更新我們組件的方法。然而走到這一步,react卻又向我們拋出了一個重大的概念——事務。batchedUpdates當中transaction.perform就是事務的調用
三、 事務與componentDidMount
了解了setState執行的全過程,我們也清楚了這個函數其實并不一定是異步去執行的,倘若沒有在進行更新dom時,它還是會立即觸發dom的更新
//this.state.val = 0 setTimeout(() => {this.setState({val: this.state})console.log(this.state.val) // 1this.setState({val: this.state})console.log(this.state.val) // 2 }, 0)當他異步的時候,從setState的源碼中我們也看到了,如果想要在新的state更新后觸發操作,只需要在setState的第二個參數當中傳入你想要執行的回調即可。
但是,如果想要解釋以下現象,我們還需要向大家介紹事務的概念。
componentDidMount () {this.setState({ val: this.state.val + 1 })console.log(this.state.val) // 0this.setState({ val: this.state.val + 1 })console.log(this.state.val) // 0}按照剛才的想法,當componentDidMount執行的時候,按理說頁面上已經有完整的dom渲染結束了,為什么此時我調用setState不能像setTimeout里一樣,立即執行對state的更新呢?下面先拋出事務的簡介。
簡單來說,事務是一種react的處理機制,通過使用wrapper包裹你實際想要調用的方法,做一些前置(initialize)和收尾(close)的工作,所以在事務包裹的方法內,會優先觸發前置鉤子,以及執行完后會有收尾方法調用,這在react用作異常處理使用。
所以此時不難想到,其實componentDidMount是react掛載dom節點的事務的收尾工作,在這個環節操作state會被阻塞,直到事務完全執行完畢后,才會重新調用更新。
四、 setState與生命周期
setState會觸發組件的更新,同時在組件生命周期的鉤子函數中我們往往會有對state的操作,操作不當很有可能發生state change =》 update =》 change state =》 state change……的死循環,那么哪些鉤子函數內使用setState是安全的呢。
我們把生命周期鉤子函數羅列出來
constructor -> componentWillMount -> render -> componentDidMount -> componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate當中constructor中本身就有state的聲明,在這里是最初的state創建,因此不需要使用setState
componentWillMount中,如果進行同步setState調用,此時的操作其實和constructor內定義state是一樣的,并不會觸發組件的額外渲染,當然這里可以做異步的setState操作,獲取頁面的初始數據。
render、shouldComponentUpdate、componentWillUpdate這三個函數中,組件還沒有渲染結束就繼續調用setState,會無限觸發更新,陷入死循環,是要注意的。
因此我們可用setState的生命周期鉤子函數有:componentWillMount、componentDidMount、componentWillReceiveProps、componentDidUpdate
至此setState的原理和使用就介紹完了,但是真正使用的契機卻往往是前端開發者需要去琢磨的,對于非控制組件,這是react中必要掌握的技術基礎了
總結
以上是生活随笔為你收集整理的React setState流程解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 有时间看看这个方法 会不会 避免 xss
- 下一篇: 性能测试压力测试