1、enqueueSetState()
非異步方法中,不管調用多少個setState
,它們都會在最後一次setState
後,放入更新隊列,而後執行一次統一的更新,詳情請參考:
React.setState之state批處理的機制 和 爲何React.setState是異步的? javascript
做用:
給React
節點的fiber
對象建立update
,並將該更新對象入隊java
源碼:react
//classComponent初始化的時候拿到的update對象
const classComponentUpdater = {
isMounted,
enqueueSetState(inst, payload, callback) {
//inst即調用this.setState時傳進來的this
//也就是classComponent實例
//經過this獲取fiber對象
//this._reactInternalFiber
//this自己有存儲 fiber對象 的屬性,叫 _reactInternalFiber
const fiber = getInstance(inst);
//計算當前時間,以前講過 不講了
const currentTime = requestCurrentTime();
//異步加載的設置,暫時不講
const suspenseConfig = requestCurrentSuspenseConfig();
//計算fiber對象的過時時間
const expirationTime = computeExpirationForFiber(
currentTime,
fiber,
suspenseConfig,
);
//建立update對象
const update = createUpdate(expirationTime, suspenseConfig);
//setState傳進來的要更新的對象
update.payload = payload;
//callback就是setState({},()=>{})的回調函數
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
}
update.callback = callback;
}
//暫時無論
if (revertPassiveEffectsChange) {
flushPassiveEffects();
}
//update入隊
enqueueUpdate(fiber, update);
//任務調度
scheduleWork(fiber, expirationTime);
},
};
複製代碼
解析:
(1)getInstance
web
//getInstance
export function get(key) {
return key._reactInternalFiber;
}
複製代碼
就是獲取目標對象的_reactInternalFiber
屬性,即this.setState
中的this
:app
(2)requestCurrentTime
,請見:React源碼解析之ReactDOM.render()異步
(3)computeExpirationForFiber
,請見:React源碼解析之ExpirationTime函數
(4)createUpdate
,請見:React源碼解析之Update和UpdateQueuepost
(5)注意下payload
,payload
就是setState
傳進來的要更新的對象this
this.setState({a:1},callback) 中的 {a:1} 即 payload
//====================
update.payload = payload;
複製代碼
(6)enqueueUpdate
,請見:React源碼解析之Update和UpdateQueuespa
(7)scheduleWork
,篇幅較長,會放在下篇講。
2、enqueueForceUpdate()
做用:
強制讓組件從新渲染,也是給React節點的fiber對象建立update,並將該更新對象入隊
源碼:
enqueueForceUpdate(inst, callback) {
const fiber = getInstance(inst);
const currentTime = requestCurrentTime();
const suspenseConfig = requestCurrentSuspenseConfig();
const expirationTime = computeExpirationForFiber(
currentTime,
fiber,
suspenseConfig,
);
const update = createUpdate(expirationTime, suspenseConfig);
//與setState不一樣的地方
//默認是0更新,須要改爲2強制更新
update.tag = ForceUpdate;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'forceUpdate');
}
update.callback = callback;
}
if (revertPassiveEffectsChange) {
flushPassiveEffects();
}
enqueueUpdate(fiber, update);
scheduleWork(fiber, expirationTime);
},
複製代碼
解析:
與enqueueSetState()
方法的流程相似,惟一不一樣的是多了個手動修改屬性tag
的值:
//與setState不一樣的地方
//默認是0更新,須要改爲2強制更新
update.tag = ForceUpdate;
複製代碼
能夠看到createUpdate()
方法中,初始化的tag
值是UpdateState
:
//建立update對象
export function createUpdate(
expirationTime: ExpirationTime,
suspenseConfig: null | SuspenseConfig,
): Update<*> {
return {
// export const UpdateState = 0;
// export const ReplaceState = 1;
// export const ForceUpdate = 2;
// export const CaptureUpdate = 3;
//重點提下CaptureUpdate,在React16後有一個ErrorBoundaries功能
//即在渲染過程當中報錯了,能夠選擇新的渲染狀態(提示有錯誤的狀態),來更新頁面
//默認是0即更新
tag: UpdateState, //0更新 1替換 2強制更新 3捕獲性的更新
};
}
複製代碼
所以要改爲ForceUpdate
,以便React
進行Update
優先級排序
3、綜上
執行setState
或forUpdate
後React
進行更新的流程爲:
(1)獲取this
上的fiber
對象
(2)計算currentTime
(3)根據(1)fiber
和(2)currentTime
計算fiber
對象的expirationTime
(4)根據(3)expirationTime
建立update
對象
(5)將setState
中要更新的對象賦值到(4)update.payload
(6)將setState
中要執行的callback
賦值到(4)update.callback
(7)update
入隊updateQueue
(8)進行任務調度
(完)