一、State的更新什麼時候是同步,什麼時候是非同步
正如我們所知,react中使用setState更新state,而state的更新大多數情況下是非同步的,但是有些情況卻是同步的。
在React中,如果是由React引發的事件處理(比如通過onClick引發的事件處理),呼叫setState不會同步更新this.state,除此之外的setState呼叫會同步執行this.state。所謂“除此之外”,指的是繞過React通過addEventListener直接新增的事件處理函式,還有通過setTimeout/setInterval產生的非同步呼叫。
二、為什麼是非同步
如果setState是同步更新state,而state的更新又會觸發元件的重新渲染,那麼每次setState都會渲染元件,這對效能是很大的消耗。所以react進行了setState的合併和批量延遲更新,正如官網所述:
三、如何實現setState的合併和批量延遲
檢視react原始碼發現,程式碼中有一個變數鎖isBatchingUpdates,isBatchingUpdates表示是否進行批量更新,初始化時預設為false,batchedUpdates方法會將isBatchingUpdates設為true
var ReactDefaultBatchingStrategy = {
isBatchingUpdates: false,
batchedUpdates: function(callback, a, b, c, d, e) {
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
if (alreadyBatchingUpdates) {
return callback(a, b, c, d, e);
} else {
return transaction.perform(callback, null, a, b, c, d, e);
}
},
};
module.exports = ReactDefaultBatchingStrategy;
複製程式碼
為了合併setState,我們需要一個佇列來儲存每次setState的資料,然後在一段時間後,清空這個佇列並渲染元件,這個佇列就是dirtyComponents。當isBatchingUpdates為true時,將會執行 dirtyComponents.push(component); 將元件push到dirtyComponents佇列。
呼叫setState()時,其實已經呼叫了ReactUpdates.batchedUpdates,此時isBatchingUpdates便是true。
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
dirtyComponents.push(component);
複製程式碼
至此,setState實現了合併和批量處理。
參考:
setState何時同步更新狀態
從零開始實現一個React(四):非同步的setState
React - setState原始碼分析(小白可讀)