react源碼解析12.狀態(tài)更新流程
視頻課程(高效學(xué)習(xí)):進(jìn)入課程
課程目錄:
6.legacy和concurrent模式入口函數(shù)
setState&forceUpdate
在react中觸發(fā)狀態(tài)更新的幾種方式:
- ReactDOM.render
- this.setState
- this.forceUpdate
- useState
- useReducer
我們重點(diǎn)看下重點(diǎn)看下this.setState和this.forceUpdate,hook在第13章講
-
this.setState內(nèi)調(diào)用this.updater.enqueueSetState,主要是將update加入updateQueue中
//ReactBaseClasses.js Component.prototype.setState = function (partialState, callback) { if (!(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null)) { { throw Error( "setState(...): takes an object of state variables to update or a function which returns an object of state variables." ); } } this.updater.enqueueSetState(this, partialState, callback, 'setState'); };
//ReactFiberClassComponent.old.js
enqueueSetState(inst, payload, callback) {
const fiber = getInstance(inst);//fiber實(shí)例
const eventTime = requestEventTime();
const suspenseConfig = requestCurrentSuspenseConfig();
const lane = requestUpdateLane(fiber, suspenseConfig);//優(yōu)先級(jí)
const update = createUpdate(eventTime, lane, suspenseConfig);//創(chuàng)建update
update.payload = payload;
if (callback !== undefined && callback !== null) { //賦值回調(diào)
update.callback = callback;
}
enqueueUpdate(fiber, update);//update加入updateQueue
scheduleUpdateOnFiber(fiber, lane, eventTime);//調(diào)度update
}
enqueueUpdate用來(lái)將update加入updateQueue隊(duì)列
//ReactUpdateQueue.old.js
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
const updateQueue = fiber.updateQueue;
if (updateQueue === null) {
return;
}
const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
const pending = sharedQueue.pending;
if (pending === null) {
update.next = update;//與自己形成環(huán)狀鏈表
} else {
update.next = pending.next;//加入鏈表的結(jié)尾
pending.next = update;
}
sharedQueue.pending = update;
}

-
this.forceUpdate和this.setState一樣,只是會(huì)讓tag賦值ForceUpdate
//ReactBaseClasses.js Component.prototype.forceUpdate = function(callback) { this.updater.enqueueForceUpdate(this, callback, 'forceUpdate'); };
//ReactFiberClassComponent.old.js
enqueueForceUpdate(inst, callback) {
const fiber = getInstance(inst);
const eventTime = requestEventTime();
const suspenseConfig = requestCurrentSuspenseConfig();
const lane = requestUpdateLane(fiber, suspenseConfig);
const update = createUpdate(eventTime, lane, suspenseConfig);
//tag賦值ForceUpdate
update.tag = ForceUpdate;
if (callback !== undefined && callback !== null) {
update.callback = callback;
}
enqueueUpdate(fiber, update);
scheduleUpdateOnFiber(fiber, lane, eventTime);
},
};
如果標(biāo)記ForceUpdate,render階段組件更新會(huì)根據(jù)checkHasForceUpdateAfterProcessing,和checkShouldComponentUpdate來(lái)判斷,如果Update的tag是ForceUpdate,則checkHasForceUpdateAfterProcessing為true,當(dāng)組件是PureComponent時(shí),checkShouldComponentUpdate會(huì)淺比較state和props,所以當(dāng)使用this.forceUpdate一定會(huì)更新
//ReactFiberClassComponent.old.js
const shouldUpdate =
checkHasForceUpdateAfterProcessing() ||
checkShouldComponentUpdate(
workInProgress,
ctor,
oldProps,
newProps,
oldState,
newState,
nextContext,
);
狀態(tài)更新整體流程

Update&updateQueue
HostRoot或者ClassComponent觸發(fā)更新后,會(huì)在函數(shù)createUpdate中創(chuàng)建update,并在后面的render階段的beginWork中計(jì)算Update。FunctionComponent對(duì)應(yīng)的Update在第11章講,它和HostRoot或者ClassComponent的Update結(jié)構(gòu)有些不一樣
//ReactUpdateQueue.old.js
export function createUpdate(eventTime: number, lane: Lane): Update<*> {//創(chuàng)建update
const update: Update<*> = {
eventTime,
lane,
tag: UpdateState,
payload: null,
callback: null,
next: null,
};
return update;
}
我們主要關(guān)注這些參數(shù):
lane:優(yōu)先級(jí)(第12章講)
tag:更新的類型,例如UpdateState、ReplaceState
payload:ClassComponent的payload是setState第一個(gè)參數(shù),HostRoot的payload是ReactDOM.render的第一個(gè)參數(shù)
callback:setState的第二個(gè)參數(shù)
next:連接下一個(gè)Update形成一個(gè)鏈表,例如同時(shí)觸發(fā)多個(gè)setState時(shí)會(huì)形成多個(gè)Update,然后用next 連接
對(duì)于HostRoot或者ClassComponent會(huì)在mount的時(shí)候使用initializeUpdateQueue創(chuàng)建updateQueue,然后將updateQueue掛載到fiber節(jié)點(diǎn)上
//ReactUpdateQueue.old.js
export function initializeUpdateQueue<State>(fiber: Fiber): void {
const queue: UpdateQueue<State> = {
baseState: fiber.memoizedState,
firstBaseUpdate: null,
lastBaseUpdate: null,
shared: {
pending: null,
},
effects: null,
};
fiber.updateQueue = queue;
}
- baseState:初始state,后面會(huì)基于這個(gè)state,根據(jù)Update計(jì)算新的state
- firstBaseUpdate、lastBaseUpdate:Update形成的鏈表的頭和尾
- shared.pending:新產(chǎn)生的update會(huì)以單向環(huán)狀鏈表保存在shared.pending上,計(jì)算state的時(shí)候會(huì)剪開(kāi)這個(gè)環(huán)狀鏈表,并且鏈接在lastBaseUpdate后
- effects:calback不為null的update
從觸發(fā)更新的fiber節(jié)點(diǎn)向上遍歷到rootFiber
在markUpdateLaneFromFiberToRoot函數(shù)中會(huì)從觸發(fā)更新的節(jié)點(diǎn)開(kāi)始向上遍歷到rootFiber,遍歷的過(guò)程會(huì)處理節(jié)點(diǎn)的優(yōu)先級(jí)(第15章講)
//ReactFiberWorkLoop.old.js
function markUpdateLaneFromFiberToRoot(
sourceFiber: Fiber,
lane: Lane,
): FiberRoot | null {
sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
let alternate = sourceFiber.alternate;
if (alternate !== null) {
alternate.lanes = mergeLanes(alternate.lanes, lane);
}
let node = sourceFiber;
let parent = sourceFiber.return;
while (parent !== null) {//從觸發(fā)更新的節(jié)點(diǎn)開(kāi)始向上遍歷到rootFiber
parent.childLanes = mergeLanes(parent.childLanes, lane);//合并childLanes優(yōu)先級(jí)
alternate = parent.alternate;
if (alternate !== null) {
alternate.childLanes = mergeLanes(alternate.childLanes, lane);
} else {
}
node = parent;
parent = parent.return;
}
if (node.tag === HostRoot) {
const root: FiberRoot = node.stateNode;
return root;
} else {
return null;
}
}
例如B節(jié)點(diǎn)觸發(fā)更新,B節(jié)點(diǎn)被被標(biāo)記為normal的update,也就是圖中的u1,然后向上遍歷到根節(jié)點(diǎn),在根節(jié)點(diǎn)上打上一個(gè)normal的update,如果此時(shí)B節(jié)點(diǎn)又觸發(fā)了一個(gè)userBlocking的Update,同樣會(huì)向上遍歷到根節(jié)點(diǎn),在根節(jié)點(diǎn)上打上一個(gè)userBlocking的update。
如果當(dāng)前根節(jié)點(diǎn)更新的優(yōu)先級(jí)是normal,u1、u2都參與狀態(tài)的計(jì)算,如果當(dāng)前根節(jié)點(diǎn)更新的優(yōu)先級(jí)是userBlocking,則只有u2參與計(jì)算

調(diào)度
在ensureRootIsScheduled中,scheduleCallback會(huì)以一個(gè)優(yōu)先級(jí)調(diào)度render階段的開(kāi)始函數(shù)performSyncWorkOnRoot或者performConcurrentWorkOnRoot
//ReactFiberWorkLoop.old.js
if (newCallbackPriority === SyncLanePriority) {
// 任務(wù)已經(jīng)過(guò)期,需要同步執(zhí)行render階段
newCallbackNode = scheduleSyncCallback(
performSyncWorkOnRoot.bind(null, root)
);
} else {
// 根據(jù)任務(wù)優(yōu)先級(jí)異步執(zhí)行render階段
var schedulerPriorityLevel = lanePriorityToSchedulerPriority(
newCallbackPriority
);
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root)
);
}
狀態(tài)更新
classComponent狀態(tài)計(jì)算發(fā)生在processUpdateQueue函數(shù)中,涉及很多鏈表操作,看圖更加直白
初始時(shí)fiber.updateQueue單鏈表上有firstBaseUpdate(update1)和lastBaseUpdate(update2),以next連接
fiber.updateQueue.shared環(huán)狀鏈表上有update3和update4,以next連接互相連接
計(jì)算state時(shí),先將fiber.updateQueue.shared環(huán)狀鏈表‘剪開(kāi)’,形成單鏈表,連接在fiber.updateQueue后面形成baseUpdate
-
然后遍歷按這條鏈表,根據(jù)baseState計(jì)算出memoizedState
react源碼12.2
帶優(yōu)先級(jí)的狀態(tài)更新
類似git提交,這里的c3意味著高優(yōu)先級(jí)的任務(wù),比如用戶出發(fā)的事件,數(shù)據(jù)請(qǐng)求,同步執(zhí)行的代碼等。
-
通過(guò)ReactDOM.render創(chuàng)建的應(yīng)用沒(méi)有優(yōu)先級(jí)的概念,類比git提交,相當(dāng)于先commit,然后提交c3
react源碼12.3 -
在concurrent模式下,類似git rebase,先暫存之前的代碼,在master上開(kāi)發(fā),然后rebase到之前的分支上
優(yōu)先級(jí)是由Scheduler來(lái)調(diào)度的,這里我們只關(guān)心狀態(tài)計(jì)算時(shí)的優(yōu)先級(jí)排序,也就是在函數(shù)processUpdateQueue中發(fā)生的計(jì)算,例如初始時(shí)有c1-c4四個(gè)update,其中c1和c3為高優(yōu)先級(jí)
- 在第一次render的時(shí)候,低優(yōu)先級(jí)的update會(huì)跳過(guò),所以只有c1和c3加入狀態(tài)的計(jì)算
- 在第二次render的時(shí)候,會(huì)以第一次中跳過(guò)的update(c2)之前的update(c1)作為baseState,跳過(guò)的update和之后的update(c2,c3,c4)作為baseUpdate重新計(jì)算
在在concurrent模式下,componentWillMount可能會(huì)執(zhí)行多次,變現(xiàn)和之前的版本不一致
注意,fiber.updateQueue.shared會(huì)同時(shí)存在于workInprogress Fiber和current Fiber,目的是為了防止高優(yōu)先級(jí)打斷正在進(jìn)行的計(jì)算而導(dǎo)致?tīng)顟B(tài)丟失,這段代碼也是發(fā)生在processUpdateQueue中

看demo_8的優(yōu)先級(jí)
現(xiàn)在來(lái)看下計(jì)算狀態(tài)的函數(shù)
//ReactUpdateQueue.old.js
export function processUpdateQueue<State>(
workInProgress: Fiber,
props: any,
instance: any,
renderLanes: Lanes,
): void {
const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
hasForceUpdate = false;
let firstBaseUpdate = queue.firstBaseUpdate;//updateQueue的第一個(gè)Update
let lastBaseUpdate = queue.lastBaseUpdate;//updateQueue的最后一個(gè)Update
let pendingQueue = queue.shared.pending;//未計(jì)算的pendingQueue
if (pendingQueue !== null) {
queue.shared.pending = null;
const lastPendingUpdate = pendingQueue;//未計(jì)算的ppendingQueue的最后一個(gè)update
const firstPendingUpdate = lastPendingUpdate.next;//未計(jì)算的pendingQueue的第一個(gè)update
lastPendingUpdate.next = null;//剪開(kāi)環(huán)狀鏈表
if (lastBaseUpdate === null) {//將pendingQueue加入到updateQueue
firstBaseUpdate = firstPendingUpdate;
} else {
lastBaseUpdate.next = firstPendingUpdate;
}
lastBaseUpdate = lastPendingUpdate;
const current = workInProgress.alternate;//current上做同樣的操作
if (current !== null) {
const currentQueue: UpdateQueue<State> = (current.updateQueue: any);
const currentLastBaseUpdate = currentQueue.lastBaseUpdate;
if (currentLastBaseUpdate !== lastBaseUpdate) {
if (currentLastBaseUpdate === null) {
currentQueue.firstBaseUpdate = firstPendingUpdate;
} else {
currentLastBaseUpdate.next = firstPendingUpdate;
}
currentQueue.lastBaseUpdate = lastPendingUpdate;
}
}
}
if (firstBaseUpdate !== null) {
let newState = queue.baseState;
let newLanes = NoLanes;
let newBaseState = null;
let newFirstBaseUpdate = null;
let newLastBaseUpdate = null;
let update = firstBaseUpdate;
do {
const updateLane = update.lane;
const updateEventTime = update.eventTime;
if (!isSubsetOfLanes(renderLanes, updateLane)) {//判斷優(yōu)先級(jí)是夠足夠
const clone: Update<State> = {//優(yōu)先級(jí)不夠 跳過(guò)當(dāng)前update
eventTime: updateEventTime,
lane: updateLane,
tag: update.tag,
payload: update.payload,
callback: update.callback,
next: null,
};
if (newLastBaseUpdate === null) {//保存跳過(guò)的update
newFirstBaseUpdate = newLastBaseUpdate = clone;
newBaseState = newState;
} else {
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
newLanes = mergeLanes(newLanes, updateLane);
} else {
//直到newLastBaseUpdate為null才不會(huì)計(jì)算,防止updateQueue沒(méi)計(jì)算完
if (newLastBaseUpdate !== null) {
const clone: Update<State> = {
eventTime: updateEventTime,
lane: NoLane,
tag: update.tag,
payload: update.payload,
callback: update.callback,
next: null,
};
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
newState = getStateFromUpdate(//根據(jù)updateQueue計(jì)算state
workInProgress,
queue,
update,
newState,
props,
instance,
);
const callback = update.callback;
if (callback !== null) {
workInProgress.flags |= Callback;//Callback flag
const effects = queue.effects;
if (effects === null) {
queue.effects = [update];
} else {
effects.push(update);
}
}
}
update = update.next;//下一個(gè)update
if (update === null) {//重置updateQueue
pendingQueue = queue.shared.pending;
if (pendingQueue === null) {
break;
} else {
const lastPendingUpdate = pendingQueue;
const firstPendingUpdate = ((lastPendingUpdate.next: any): Update<State>);
lastPendingUpdate.next = null;
update = firstPendingUpdate;
queue.lastBaseUpdate = lastPendingUpdate;
queue.shared.pending = null;
}
}
} while (true);
if (newLastBaseUpdate === null) {
newBaseState = newState;
}
queue.baseState = ((newBaseState: any): State);//新的state
queue.firstBaseUpdate = newFirstBaseUpdate;//新的第一個(gè)update
queue.lastBaseUpdate = newLastBaseUpdate;//新的最后一個(gè)update
markSkippedUpdateLanes(newLanes);
workInProgress.lanes = newLanes;
workInProgress.memoizedState = newState;
}
//...
}

