網上關於react setState的結論很多,好比:react
但你是否真的瞭解setState背後的機制?真的是setState觸發的刷新嗎?
廢話不說,先上圖
app
組件掛載後,setState通常是經過DOM交互事件觸發。這裏以click
爲例,其餘也同樣。函數
代碼很簡單this
import React, {Component} from 'react'; class MyInfo extends Component{ constructor(props,context){ super(props,context); this.state = { age:1 } } _grow(age){ age++ this.setState({ age:age }) } render(){ const {age} = this.state return ( <div> 個人年齡是{age} <button onClick={this._grow.bind(this,age)}>點擊漲一歲</button> </div> ) } } export default MyInfo;
咱們點擊button按鈕時,到底發生了什麼?ReactEventListener會觸發dispatchEvent方法。(具體怎麼觸發是事件機制的事,這裏不深究)spa
dispatchEvent: function (topLevelType, nativeEvent) { if (!ReactEventListener._enabled) { return; } var bookKeeping = TopLevelCallbackBookKeeping.getPooled(topLevelType, nativeEvent); try { // Event queue being processed in the same cycle allows // `preventDefault`. ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping); } finally { TopLevelCallbackBookKeeping.release(bookKeeping); } }
能夠看到這裏有個ReactUpdates.batchedUpdates
方法。咱們跟進去看看prototype
function batchedUpdates(callback, a, b, c, d, e) { ensureInjected(); return batchingStrategy.batchedUpdates(callback, a, b, c, d, e); }
能夠發現這裏調用了batchingStrategy
的方法。這又是什麼鬼,其實這是注入進來的ReactDefaultBatchingStragy
(這裏插一句,React大量運用了注入機制,這樣每次注入的都是同一個實例化對象,防止屢次實例化。)
到這邊就已經開啓了批量更新模式
繼續看,code
batchedUpdates: function(callback, a, b, c, d, e) { var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; ReactDefaultBatchingStrategy.isBatchingUpdates = true; // The code is written this way to avoid extra allocations if (alreadyBatchingUpdates) { return callback(a, b, c, d, e); } else { return transaction.perform(callback, null, a, b, c, d, e); } },
transaction.perform
執行了一個事務。事務其餘文章說的不少我就不詳細解釋了。大概就是,transaction在執行perform以前會執行特性的initialize方法,而後執行傳進去的callback,以後會執行close方法,是否是似曾相識?沒錯,高階函數或者高階組件都是這路數。
在ReactDefaultBatchingStragy
裏能夠發現orm
var transaction = new ReactDefaultBatchingStrategyTransaction(); //在事務結束時清理一下標識 var RESET_BATCHED_UPDATES = { initialize: emptyFunction, close: function() { ReactDefaultBatchingStrategy.isBatchingUpdates = false; }, }; // 在事務結束時執行flushBatchedUpdates方法,這個方法就是 state 更新的核心代碼了。 var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction, close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates), }; var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES]; function ReactDefaultBatchingStrategyTransaction() { this.reinitializeTransaction(); } Object.assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, { getTransactionWrappers: function() { return TRANSACTION_WRAPPERS; }, });
能夠發現這個transition有兩個wrapper,主要看FLUSH_BATCHED_UPDATES
對象
目前走完了圖中的第一行,有點暈的能夠對着圖回顧一下。blog
-
是否是發現哪裏不對?是的,到如今咱們的setState還沒執行呢!
接着上文,咱們首先看看transition的兩個initailize方法,發現時兩個空函數。跳過
接着就是perform須要執行的邏輯了。再次放出代碼
ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
也就是執行這邊的handleTopLevelImpl
。正是在這邊調用DOM事件對應的回調方法。也就是例子中_grow
在這時候調用。
而後是setState方法。這裏和大部分書和文章說的差很少。拋開細節就是將state的變化和對應的回調函數放置到_pendingStateQueue
,和_pendingCallback
中。
而後把須要更新的組件放到dirtyComponents序列中。
重點來了:
注意注意!!!!
setState歷來不負責更新操做。它的工做只是把state,和callback放進序列,而且把要更新的組件放到dirtyComponents序列
還記得嗎?咱們還在ReactDefalutBatchingStragy
的事務中,perform執行完了,還要執行close。
真正執行更新方法的是close裏面的flushBatchedUpdates
。鑑於文章長度,其餘的能夠看圖理解