轉自IMWeb社區,做者:黃qiong, 原文連接
學過react的人都知道,setState在react裏是一個很重要的方法,使用它能夠更新咱們數據的狀態,本篇文章從簡單使用到深刻到setState的內部,全方位爲你揭開setState的神祕面紗~react
setState(updater, callback)
這個方法是用來告訴react組件數據有更新,有可能須要從新渲染。它是異步的,react一般會集齊一批須要更新的組件,而後一次性更新來保證渲染的性能,因此這就給咱們埋了一個坑:web
那就是在使用setState
改變狀態以後,馬上經過this.state
去拿最新的狀態每每是拿不到的。數組
因此第一個使用要點就是:若是你須要基於最新的state作業務的話,能夠在componentDidUpdate
或者setState
的回調函數裏獲取。(注:官方推薦第一種作法)bash
// setState回調函數
changeTitle: function (event) {
this.setState({ title: event.target.value }, () => this.APICallFunction());
},
APICallFunction: function () {
// Call API with the updated value
}
複製代碼
設想有一個需求,須要在在onClick裏累加兩次,以下app
onClick = () => {
this.setState({ index: this.state.index + 1 });
this.setState({ index: this.state.index + 1 });
}
複製代碼
在react眼中,這個方法最終會變成異步
Object.assign(
previousState,
{index: state.index+ 1},
{index: state.index+ 1},
...
)
複製代碼
因爲後面的數據會覆蓋前面的更改,因此最終只加了一次.因此若是是下一個state依賴前一個state的話,推薦給setState傳function函數
onClick = () => {
this.setState((prevState, props) => {
return {quantity: prevState.quantity + 1};
});
this.setState((prevState, props) => {
return {quantity: prevState.quantity + 1};
});
}
複製代碼
以上是使用setState的兩個注意事項,接下來咱們來看看setState被調用以後,更新組件的過程,下面是一個簡單的流程圖。性能
下面來逐步的解析圖裏的流程。ui
ReactComponent.prototype.setState = function (partialState, callback) {
// 將setState事務放進隊列中
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
複製代碼
這裏的partialState能夠傳object,也能夠傳function,它會產生新的state以一種Object.assgine()
的方式跟舊的state進行合併。this
enqueueSetState: function (publicInstance, partialState) {
// 獲取當前組件的instance
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
// 將要更新的state放入一個數組裏
var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
queue.push(partialState);
// 將要更新的component instance也放在一個隊列裏
enqueueUpdate(internalInstance);
}
複製代碼
這段代碼能夠得知,enqueueSetState 作了兩件事: 一、將新的state放進數組裏 二、用enqueueUpdate來處理將要更新的實例對象
function enqueueUpdate(component) {
// 若是沒有處於批量建立/更新組件的階段,則處理update state事務
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
// 若是正處於批量建立/更新組件的過程,將當前的組件放在dirtyComponents數組中
dirtyComponents.push(component);
}
複製代碼
由這段代碼能夠看到,當前若是正處於建立/更新組件的過程,就不會馬上去更新組件,而是先把當前的組件放在dirtyComponent裏,因此不是每一次的setState都會更新組件~。
這段代碼就解釋了咱們經常據說的:setState是一個異步的過程,它會集齊一批須要更新的組件而後一塊兒更新。
而batchingStrategy 又是個什麼東西呢?
var ReactDefaultBatchingStrategy = {
// 用於標記當前是否出於批量更新
isBatchingUpdates: false,
// 當調用這個方法時,正式開始批量更新
batchedUpdates: function (callback, a, b, c, d, e) {
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
// 若是當前事務正在更新過程在中,則調用callback,既enqueueUpdate
if (alreadyBatchingUpdates) {
return callback(a, b, c, d, e);
} else {
// 不然執行更新事務
return transaction.perform(callback, null, a, b, c, d, e);
}
}
};
複製代碼
這裏注意兩點: 一、若是當前事務正在更新過程當中,則使用enqueueUpdate
將當前組件放在dirtyComponent
裏。 二、若是當前不在更新過程的話,則執行更新事務。
/**
* <pre>
* wrappers (injected at creation time)
* + +
* | |
* +-----------------|--------|--------------+
* | v | |
* | +---------------+ | |
* | +--| wrapper1 |---|----+ |
* | | +---------------+ v | |
* | | +-------------+ | |
* | | +----| wrapper2 |--------+ |
* | | | +-------------+ | | |
* | | | | | |
* | v v v v | wrapper
* | +---+ +---+ +---------+ +---+ +---+ | invariants
* perform(anyMethod) | | | | | | | | | | | | maintained
* +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | +---+ +---+ +---------+ +---+ +---+ |
* | initialize close |
* +-----------------------------------------+
* </pre>
*/
複製代碼
簡單說明一下transaction對象,它暴露了一個perform的方法,用來執行anyMethod,在anyMethod執行的前,須要先執行全部wrapper的initialize方法,在執行完後,要執行全部wrapper的close方法,就辣麼簡單。
在ReactDefaultBatchingStrategy.js,tranction 的 wrapper有兩個 FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES
var RESET_BATCHED_UPDATES = {
initialize: emptyFunction,
close: function () {
ReactDefaultBatchingStrategy.isBatchingUpdates = false;
}
};
var FLUSH_BATCHED_UPDATES = {
initialize: emptyFunction,
close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
複製代碼
能夠看到,這兩個wrapper的initialize
都沒有作什麼事情,可是在callback執行完以後,RESET_BATCHED_UPDATES 的做用是將isBatchingUpdates置爲false, FLUSH_BATCHED_UPDATES 的做用是執行flushBatchedUpdates,而後裏面會循環全部dirtyComponent,調用updateComponent來執行全部的生命週期方法,componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate 最後實現組件的更新。
以上即爲setState的實現過程,最後仍是用一個流程圖在作一個總結吧~
參考文檔: