path:packages/react-reconciler/src/ReactUpdateQueue.jshtml
export type Update<State> = {
expirationTime: ExpirationTime, // 到期時間
tag: 0 | 1 | 2 | 3, // 更新類型
payload: any, // 負載
callback: (() => mixed) | null, // 回調函數
next: Update<State> | null, // 下一個更新
nextEffect: Update<State> | null, // 下一個效果
};
複製代碼
React 的狀態更新分爲四種狀況,他們分別對應 Update 的 tag 屬性的四個值:react
export const UpdateState = 0; // 更新狀態
export const ReplaceState = 1; // 替換狀態
export const ForceUpdate = 2; // 強制更新
export const CaptureUpdate = 3; // 捕獲更新
複製代碼
/** * 建立更新 * @param expirationTime * @returns {{next: null, payload: null, expirationTime: ExpirationTime, callback: null, tag: number, nextEffect: null}} */
export function createUpdate(expirationTime: ExpirationTime): Update<*> {
return {
expirationTime: expirationTime,
tag: UpdateState,
payload: null,
callback: null,
next: null,
nextEffect: null,
};
}
複製代碼
調用此方法建立的更新默認爲是局部更新,須要合併先後狀態。數據結構
export type UpdateQueue<State> = {
baseState: State,
firstUpdate: Update<State> | null,
lastUpdate: Update<State> | null,
firstCapturedUpdate: Update<State> | null,
lastCapturedUpdate: Update<State> | null,
firstEffect: Update<State> | null,
lastEffect: Update<State> | null,
firstCapturedEffect: Update<State> | null,
lastCapturedEffect: Update<State> | null,
};
複製代碼
/** * 建立更新隊列 * @param baseState * @returns {UpdateQueue<State>} */
export function createUpdateQueue<State>(baseState: State): UpdateQueue<State> {
const queue: UpdateQueue<State> = {
baseState,
firstUpdate: null,
lastUpdate: null,
firstCapturedUpdate: null,
lastCapturedUpdate: null,
firstEffect: null,
lastEffect: null,
firstCapturedEffect: null,
lastCapturedEffect: null,
};
return queue;
}
複製代碼
從上面的代碼中能夠看到,更新隊列是一個單向鏈表:app
追加更新到鏈表尾部less
/** * 添加更新到隊列中 * @param queue * @param update */
function appendUpdateToQueue<State>( queue: UpdateQueue<State>, update: Update<State>, ) {
// 將更新追加到列表的末尾。
if (queue.lastUpdate === null) {
// 隊列是空的
queue.firstUpdate = queue.lastUpdate = update;
} else {
queue.lastUpdate.next = update;
queue.lastUpdate = update;
}
}
複製代碼
每次更新的時候須要根據不一樣的更新類型來獲取下一次的 state:dom
/** * 從跟新獲取狀態 * @param workInProgress * @param queue * @param update * @param prevState * @param nextProps * @param instance * @returns {State|*} */
function getStateFromUpdate<State>( workInProgress: Fiber, queue: UpdateQueue<State>, update: Update<State>, prevState: State, nextProps: any, instance: any, ): any {
switch (update.tag) {
case ReplaceState: {
const payload = update.payload;
if (typeof payload === 'function') {
// 更新器函數
const nextState = payload.call(instance, prevState, nextProps);
return nextState;
}
// 狀態對象
return payload;
}
case CaptureUpdate: {
workInProgress.effectTag =
(workInProgress.effectTag & ~ShouldCapture) | DidCapture;
}
// Intentional fallthrough
case UpdateState: {
const payload = update.payload;
let partialState;
if (typeof payload === 'function') {
// Updater function
partialState = payload.call(instance, prevState, nextProps);
} else {
// 部分狀態對象
partialState = payload;
}
if (partialState === null || partialState === undefined) {
// Null 和 undefined 被視爲 no-ops。
return prevState;
}
// 合併部分狀態和前一個狀態。
return Object.assign({}, prevState, partialState);
}
case ForceUpdate: {
hasForceUpdate = true;
return prevState;
}
}
return prevState;
}
複製代碼
從上面的代碼能夠看到,更新 state 時能夠接收一個更新器函數,這個更新器函數被綁定到當前的實例上運行,也就是在 React 文檔 中寫到的,setState
能夠接收一個函數做爲參數:異步
setState((prevState, nextProps) => {
// do something
})
複製代碼
prevState
參數是上一次調用 setState
以後的狀態,而不是已經更新到 dom 中的狀態,由於狀態更新是異步的,爲了不沒必要要的從新渲染來提高性能。nextProps
參數是下一次的 props 對象/** * * @param workInProgress * @param queue * @param props * @param instance * @param renderExpirationTime */
export function processUpdateQueue<State>( workInProgress: Fiber, queue: UpdateQueue<State>, props: any, instance: any, renderExpirationTime: ExpirationTime, ): void {
hasForceUpdate = false;
// 確保處理的更新隊列的 work 是一個複製品
queue = ensureWorkInProgressQueueIsAClone(workInProgress, queue);
if (__DEV__) {
currentlyProcessingQueue = queue;
}
// These values may change as we process the queue.
// 當咱們處理隊列時,這些值可能會改變。
let newBaseState = queue.baseState;
let newFirstUpdate = null;
let newExpirationTime = NoWork;
// Iterate through the list of updates to compute the result.
// 迭代更新列表以計算結果。
let update = queue.firstUpdate;
let resultState = newBaseState;
while (update !== null) {
const updateExpirationTime = update.expirationTime;
if (updateExpirationTime < renderExpirationTime) {
// This update does not have sufficient priority. Skip it.
// 此更新沒有足夠的優先級。跳過它。
if (newFirstUpdate === null) {
// This is the first skipped update. It will be the first update in
// the new list.
// 這是第一個跳過的更新。這將是新列表中的第一個更新。
newFirstUpdate = update;
// Since this is the first update that was skipped, the current result
// is the new base state.
// 因爲這是跳過的第一個更新,因此當前結果是 new base state。
newBaseState = resultState;
}
// Since this update will remain in the list, update the remaining
// expiration time.
// 因爲此更新將保留在列表中,因此更新剩餘的過時時間。
if (newExpirationTime < updateExpirationTime) {
newExpirationTime = updateExpirationTime;
}
} else {
// This update does have sufficient priority. Process it and compute
// a new result.
// 此次更新確實有足夠的優先級。處理它並計算一個新的結果。
resultState = getStateFromUpdate(
workInProgress,
queue,
update,
resultState,
props,
instance,
);
const callback = update.callback;
if (callback !== null) {
workInProgress.effectTag |= Callback;
// Set this to null, in case it was mutated during an aborted render.
// 將其設置爲null,以防在停止渲染期間發生突變。
update.nextEffect = null;
if (queue.lastEffect === null) {
queue.firstEffect = queue.lastEffect = update;
} else {
queue.lastEffect.nextEffect = update;
queue.lastEffect = update;
}
}
}
// Continue to the next update.
// 繼續下一個更新。
update = update.next;
}
// Separately, iterate though the list of captured updates.
// 另外,遍歷捕獲的更新列表。
let newFirstCapturedUpdate = null;
update = queue.firstCapturedUpdate;
while (update !== null) {
const updateExpirationTime = update.expirationTime;
if (updateExpirationTime < renderExpirationTime) {
// This update does not have sufficient priority. Skip it.
// 這個更新沒有足夠的優先級。跳過它。
if (newFirstCapturedUpdate === null) {
// This is the first skipped captured update. It will be the first
// update in the new list.
// 這是第一次跳過捕獲的更新。這將是新列表中的第一個更新。
newFirstCapturedUpdate = update;
// If this is the first update that was skipped, the current result is
// the new base state.
// 若是這是跳過的第一個更新,則當前結果是新的基本狀態。
if (newFirstUpdate === null) {
newBaseState = resultState;
}
}
// Since this update will remain in the list, update the remaining
// expiration time.
// 因爲此更新將保留在列表中,因此更新剩餘的過時時間。
if (newExpirationTime < updateExpirationTime) {
newExpirationTime = updateExpirationTime;
}
} else {
// This update does have sufficient priority. Process it and compute
// a new result.
// 此次更新確實有足夠的優先級。處理它並計算一個新的結果。
resultState = getStateFromUpdate(
workInProgress,
queue,
update,
resultState,
props,
instance,
);
const callback = update.callback;
if (callback !== null) {
workInProgress.effectTag |= Callback;
// Set this to null, in case it was mutated during an aborted render.
// 將其設置爲 null,以防在停止 render 期間發生突變。
update.nextEffect = null;
if (queue.lastCapturedEffect === null) {
queue.firstCapturedEffect = queue.lastCapturedEffect = update;
} else {
queue.lastCapturedEffect.nextEffect = update;
queue.lastCapturedEffect = update;
}
}
}
update = update.next;
}
if (newFirstUpdate === null) {
queue.lastUpdate = null;
}
if (newFirstCapturedUpdate === null) {
queue.lastCapturedUpdate = null;
} else {
workInProgress.effectTag |= Callback;
}
if (newFirstUpdate === null && newFirstCapturedUpdate === null) {
// We processed every update, without skipping. That means the new base
// state is the same as the result state.
// 咱們處理了每一個更新,沒有跳過。這意味着新的基狀態與結果狀態相同。
newBaseState = resultState;
}
queue.baseState = newBaseState;
queue.firstUpdate = newFirstUpdate;
queue.firstCapturedUpdate = newFirstCapturedUpdate;
// Set the remaining expiration time to be whatever is remaining in the queue.
// This should be fine because the only two other things that contribute to
// expiration time are props and context. We're already in the middle of the
// begin phase by the time we start processing the queue, so we've already
// dealt with the props. Context in components that specify
// shouldComponentUpdate is tricky; but we'll have to account for
// that regardless.
// 將剩餘的過時時間設置爲隊列中剩餘的時間。
// 這應該沒問題,由於影響過時時間的另外兩個因素是 props 和 context。
// 在開始處理隊列時,咱們已經處於 begin 階段的中間,
// 因此咱們已經處理了這些 props。
// 指定 shouldComponentUpdate 的組件中的 Context 比較複雜;
// 但不管如何咱們都要考慮到這一點。
workInProgress.expirationTime = newExpirationTime;
workInProgress.memoizedState = resultState;
if (__DEV__) {
currentlyProcessingQueue = null;
}
}
複製代碼
/** * 提交更新隊列 * @param finishedWork * @param finishedQueue * @param instance * @param renderExpirationTime */
export function commitUpdateQueue<State>( finishedWork: Fiber, finishedQueue: UpdateQueue<State>, instance: any, renderExpirationTime: ExpirationTime, ): void {
// 若是已完成的渲染包含捕獲的更新,
// 而且仍然有較低優先級的更新遺留下來,
// 那麼咱們須要將捕獲的更新保存在隊列中,
// 以便在以較低優先級再次處理隊列時從新基於它們,而不是丟棄它們。
if (finishedQueue.firstCapturedUpdate !== null) {
// 將捕獲的更新列表鏈接到普通列表的末尾。
if (finishedQueue.lastUpdate !== null) {
finishedQueue.lastUpdate.next = finishedQueue.firstCapturedUpdate;
finishedQueue.lastUpdate = finishedQueue.lastCapturedUpdate;
}
// 清除捕獲的更新列表。
finishedQueue.firstCapturedUpdate = finishedQueue.lastCapturedUpdate = null;
}
// 提交效果
commitUpdateEffects(finishedQueue.firstEffect, instance);
finishedQueue.firstEffect = finishedQueue.lastEffect = null;
commitUpdateEffects(finishedQueue.firstCapturedEffect, instance);
finishedQueue.firstCapturedEffect = finishedQueue.lastCapturedEffect = null;
}
複製代碼
/** * 提交更新效果 * @param effect * @param instance */
function commitUpdateEffects<State>( effect: Update<State> | null, instance: any, ): void {
while (effect !== null) {
const callback = effect.callback;
if (callback !== null) {
effect.callback = null;
callCallback(callback, instance);
}
effect = effect.nextEffect;
}
}
複製代碼
/** * 確保工做中的處理隊列是複製品 * 1. 判斷當前隊列和更新隊列是否是相等 * 2. 若相等則克隆,若不等則返回當前隊列 * @param workInProgress * @param queue * @returns {UpdateQueue<State>} */
function ensureWorkInProgressQueueIsAClone<State>( workInProgress: Fiber, queue: UpdateQueue<State>, ): UpdateQueue<State> {
const current = workInProgress.alternate;
if (current !== null) {
// 若是正在工做的隊列等於當前隊列,咱們須要首先克隆它。
if (queue === current.updateQueue) {
queue = workInProgress.updateQueue = cloneUpdateQueue(queue);
}
}
return queue;
}
複製代碼
/** * 克隆更新隊列 * @param currentQueue * @returns {UpdateQueue<State>} */
function cloneUpdateQueue<State>( currentQueue: UpdateQueue<State>, ): UpdateQueue<State> {
const queue: UpdateQueue<State> = {
baseState: currentQueue.baseState,
firstUpdate: currentQueue.firstUpdate,
lastUpdate: currentQueue.lastUpdate,
// TODO: With resuming, if we bail out and resuse the child tree, we should
// keep these effects.
firstCapturedUpdate: null,
lastCapturedUpdate: null,
firstEffect: null,
lastEffect: null,
firstCapturedEffect: null,
lastCapturedEffect: null,
};
return queue;
}
複製代碼
/** * 排隊更新 * @param fiber * @param update */
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
// 更新隊列是惰性建立的。
const alternate = fiber.alternate;
let queue1;
let queue2;
if (alternate === null) {
// 只有一個 fiber
queue1 = fiber.updateQueue;
queue2 = null;
if (queue1 === null) {
queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
}
} else {
// 有兩個 owner。
queue1 = fiber.updateQueue;
queue2 = alternate.updateQueue;
if (queue1 === null) {
if (queue2 === null) {
// Neither fiber has an update queue. Create new ones.
// 這兩種 fiber 都沒有更新隊列。創造一個新隊列。
queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
queue2 = alternate.updateQueue = createUpdateQueue(
alternate.memoizedState,
);
} else {
// Only one fiber has an update queue. Clone to create a new one.
// 只有一個 fiber 有更新隊列。克隆以建立一個新的。
queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);
}
} else {
if (queue2 === null) {
// Only one fiber has an update queue. Clone to create a new one.
// 只有一個 fiber 有更新隊列。克隆以建立一個新的。
queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);
} else {
// Both owners have an update queue.
// 兩個全部者都有一個更新隊列。
}
}
}
if (queue2 === null || queue1 === queue2) {
// There's only a single queue.
// 只有一個隊列。
appendUpdateToQueue(queue1, update);
} else {
// There are two queues. We need to append the update to both queues,
// while accounting for the persistent structure of the list — we don't
// want the same update to be added multiple times.
// 有兩個隊列。咱們須要將更新附加到兩個隊列,
// 同時考慮到列表的持久結構——咱們不但願將相同的更新添加屢次。
if (queue1.lastUpdate === null || queue2.lastUpdate === null) {
// One of the queues is not empty. We must add the update to both queues.
// 其中一個隊列不是空的。咱們必須將更新添加到兩個隊列。
appendUpdateToQueue(queue1, update);
appendUpdateToQueue(queue2, update);
} else {
// Both queues are non-empty. The last update is the same in both lists,
// because of structural sharing. So, only append to one of the lists.
// 兩個隊列都不是空的。因爲結構共享,這兩個列表中的最新更新是相同的。
// 所以,只向其中一個列表追加。
appendUpdateToQueue(queue1, update);
// But we still need to update the `lastUpdate` pointer of queue2.
// 可是咱們仍然須要更新 queue2 的 `lastUpdate` 指針。
queue2.lastUpdate = update;
}
}
if (__DEV__) {
if (
fiber.tag === ClassComponent &&
(currentlyProcessingQueue === queue1 ||
(queue2 !== null && currentlyProcessingQueue === queue2)) &&
!didWarnUpdateInsideUpdate
) {
warningWithoutStack(
false,
'An update (setState, replaceState, or forceUpdate) was scheduled ' +
'from inside an update function. Update functions should be pure, ' +
'with zero side-effects. Consider using componentDidUpdate or a ' +
'callback.',
);
didWarnUpdateInsideUpdate = true;
}
}
}
複製代碼
/** * 排隊捕獲的更新 * @param workInProgress * @param update */
export function enqueueCapturedUpdate<State>( workInProgress: Fiber, update: Update<State>, ) {
// 捕獲的更新進入一個單獨的列表,而且只在正在進行的隊列中。
let workInProgressQueue = workInProgress.updateQueue;
if (workInProgressQueue === null) {
workInProgressQueue = workInProgress.updateQueue = createUpdateQueue(
workInProgress.memoizedState,
);
} else {
// TODO:我把它放在這裏,而不是 createWorkInProgress,這樣咱們就不會沒必要要地克隆隊列。也許有更好的方法來構造它。。
workInProgressQueue = ensureWorkInProgressQueueIsAClone(
workInProgress,
workInProgressQueue,
);
}
// Append the update to the end of the list.
// 將更新追加到列表的末尾。
if (workInProgressQueue.lastCapturedUpdate === null) {
// This is the first render phase update
// 這是第一個渲染階段的更新
workInProgressQueue.firstCapturedUpdate = workInProgressQueue.lastCapturedUpdate = update;
} else {
workInProgressQueue.lastCapturedUpdate.next = update;
workInProgressQueue.lastCapturedUpdate = update;
}
}
複製代碼
/** * 調用回調 * 1. 回調不存在則拋出錯誤 * 2. 回調存在則使用上下文執行回調 * * @param callback * @param context */
function callCallback(callback, context) {
invariant(
typeof callback === 'function',
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: %s',
callback,
);
callback.call(context);
}
複製代碼