<!-- TOC -->html
A1react
<!-- /TOC -->segmentfault
setState改變狀態以後,不會當即更新state值。因此,若是改變state值,react是何時進行組件的更新呢?setState()到底作了一些什麼呢?數組
引入一段源碼app
react中定義的setState方法,定義了兩個參數(partialState,callback)。函數
partialState: 新的state值;
callback: 回調函數。post
getInternalInstanceReadyForUpdate方法的目的是獲取當前組件對象,將其賦值給internalInstance變量。接下來判斷當前組件對象的state更新隊列是否存在,若是存在則將partialState也就是新的state值加入隊列;若是不存在,則建立該對象的更新隊列。而後進入enqueueUpdate方法。this
enqueueCallback也是先獲取當前組件對象,若是已經存在其餘回調,就加入等待回調隊列,若是當前沒有回調,就建立等待回調隊列。而後進入enqueueUpdate方法。spa
能夠發現,enqueueSetState&enqueueCallback最終都是進入enqueueUpdate方法。下面咱們來看看enqueueUpdate方法。調試
官方註解是:給組件作個標記:須要從新渲染,而且將可選的回調函數添加到函數列表中,這些函數將在從新渲染的時候執行。
咱們看一下函數具體作了哪些事。發現這個函數只是作了一個判斷:若是batchingStrategy.isBatchingUpdates爲false,就執行batchingStrategy.batchedUpdates(enqueueUpdate,component),不然就加入dirtyComponents。
這裏提到batchingStrategy,批量更新策略。
批量更新策略是什麼呢?看代碼發現batchingStrategy批量更新策略只是一個簡單的對象,定義了一個 isBatchingUpdates 的布爾值和一個 batchedUpdates 方法。默認isBatchingUpdates(下面稱爲更新標誌)爲false,而後會進入batchedUpdates方法,先把更新標誌isBatchingUpdates設爲true,而後執行transaction.perform(callback),即transaction.perform(enqueueUpdate)。
React內部採用了"狀態機"的概念,組件處於不一樣的狀態時,所執行的邏輯也並不相同。以組件更新流程爲例,React以事務+狀態的形式對組件進行更新。
經過上面的一部分代碼,咱們發現setState()方法主要是enqueueUpdate()進行狀態更新,怎樣進行狀態更新呢?定義了一個批量更新策略:判斷更新標誌isBatchingUpdates的值,若是爲false,調用batchedUpdates()-->(先把更新標誌isBatchingUpdates改成true,而後調用transaction.perform(enqueueUpdate))。若是爲true,就把組件加入dirtyComponents數組中。
React內部採用了"狀態機"的概念,組件處於不一樣的狀態時,所執行的邏輯也並不相同。以組件更新流程爲例,React以事務+狀態的形式對組件進行更新,所以接下來咱們看看事務的機制。
wrappers (injected at creation time) + + | | +-----------------|--------|--------------+ | v | | | +---------------+ | | | +--| wrapper1 |---|----+ | | | +---------------+ v | | | | +-------------+ | | | | +----| wrapper2 |--------+ | | | | +-------------+ | | | | | | | | | | v v v v | wrapper | +---+ +---+ +---------+ +---+ +---+ | invariants perform(anyMethod) | | | | | | | | | | | | maintained +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|--------> | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +---+ +---+ +---------+ +---+ +---+ | | initialize close | +-----------------------------------------+
這是官方代碼的解析圖。
能夠看出調用函數是perform(anyMethod),而後方法anyMethod被wrapper包裹了,wrapper依次執行了initialize->anyMethod->close
function anyMethod(){ console.log('xx') }; transaction.perform(anyMethod);
代碼的執行順序是
initialize() 輸出xx close()
因此這裏wrapper是怎樣定義的呢?
第二個wrapper比較簡單,先來看一下第二個wrapper。
第二個wrapper(RESET_BATCHED_UPDATES)的做用是將更新標誌isBatchingUpdates重置爲false;個人理解這裏是收集完全部要更新的state值,都加入_pendingStateQueue待更新狀態隊列了,而後組件更新完了以後,將更新標誌重置爲false,等待下次更新。而後下面來看一下第一個wrapper。
5&f=png&s=54081)
第一個wrapper主要的做用是更新組件,執行了ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)。
能夠看到flushBatchedUpdates方法循環遍歷全部的dirtyComponents,又經過事務的形式調用runBatchedUpdates方法。
一共作了兩件事:
而後看一下updateComponent方法,官方註釋是:更新組件,會調用shouldComponentUpdate,而後調用剩餘的生命週期函數,更新DOM結構。
這裏終於更新了組件。看代碼會發如今shouldComponentUpdate以前,執行了_processPendingState方法,該方法主要對state進行處理:
綜上說明了,在一個生命週期內,在componentShouldUpdate執行以前,全部的state變化都會被合併,最後統一處理。
綜上,
那按照上述說的批量更新,第一次setState-->進入enqueueUpdate()-->此時isBatchingUpdates默認爲false-->batchedUpdates(enqueueUpdate,...)-->設置isBatchingUpdates爲true;transaction.perform(enqueueUpdates);-->(第一個wrapper:FLUSH_BATCHED_UPDATES)組件更新-->(第二個wrapper:RESET_BATCHED_UPDATES的close方法)設置isBatchingUpdates爲false-->第二次setState-->isBatchingUpdates爲false-->..-->組件更新-->isBatchingUpdates恢復爲false。
這樣和結果不對呀?按上述邏輯的話,豈不是每次setState都會更新this.state的值?
調試代碼會發現,原來整個將 React 組件渲染到 DOM 中的過程就處於一個大的 Transaction 中。
在進入生命週期以前,就會調用batchedUpdates(),因此此時isBatchingUpdates已經修改成true了。後面第一次進入setState()時,就會進入加入dirtyComponent中。因此這也就是爲何兩次打印 this.state.foods 都是 '' 的緣由,新的 state 尚未被應用到組件中。
在render函數裏,沒法setState
在render函數中不能setState()。
從react生命週期能夠看出:state更新會從新觸發render(),因此會致使setState()-->re-render()-->setState()--re-render()-->...-->setState()-->re-render(),一直循環往復。
因此,同理在state更新的生命週期的函數中(componentWillUpdate/componentDidUpdate),都不能setState()
參考資料
https://juejin.im/post/59cc4c...
https://zh-hans.reactjs.org/d...