ReactFiberUpdateQueue
insertUpdateIntoFiber
It takes fiber element and update object and create queues by calling ensureUpdateQueues
and set update info into queues by insertUpdateIntoQueue
. update
object which is generated here will look like this.
const update = {
expirationTime,
partialState: {element},
callback,
isReplace: false,
isForced: false,
capturedValue: null,
next: null,
};
let q1;
let q2;
export function insertUpdateIntoFiber<State>(
fiber: Fiber,
update: Update<State>,
): void {
ensureUpdateQueues(fiber);
const queue1: Fiber = (q1: any);
const queue2: Fiber | null = (q2: any);
// If there's only one queue, add the update to that queue and exit.
if (queue2 === null) {
insertUpdateIntoQueue(queue1, update);
return;
}
// If either queue is empty, we need to add to both queues.
if (queue1.last === null || queue2.last === null) {
insertUpdateIntoQueue(queue1, update);
insertUpdateIntoQueue(queue2, update);
return;
}
// If both lists are not empty, the last update is the same for both lists
// because of structural sharing. So, we should only append to one of
// the lists.
insertUpdateIntoQueue(queue1, update);
// But we still need to update the `last` pointer of queue2.
queue2.last = update;
}
ensureUpdateQueues
This function is invoked from insertUpdateIntoFiber
function. This generates queues by calling createUpdateQueue
and assigns them to q1 and q2.
export function ensureUpdateQueues(fiber: Fiber) {
q1 = q2 = null;
// We'll have at least one and at most two distinct update queues.
const alternateFiber = fiber.alternate;
let queue1 = fiber.updateQueue;
if (queue1 === null) {
// TODO: We don't know what the base state will be until we begin work.
// It depends on which fiber is the next current. Initialize with an empty
// base state, then set to the memoizedState when rendering. Not super
// happy with this approach.
queue1 = fiber.updateQueue = createUpdateQueue((null: any));
}
let queue2;
if (alternateFiber !== null) {
queue2 = alternateFiber.updateQueue;
if (queue2 === null) {
queue2 = alternateFiber.updateQueue = createUpdateQueue((null: any));
}
} else {
queue2 = null;
}
queue2 = queue2 !== queue1 ? queue2 : null;
// Use module variables instead of returning a tuple
q1 = queue1;
q2 = queue2;
}
createUpdateQueue
Singly linked-list of updates. When an update is scheduled, it is added to the queue of the current fiber and the work-in-progress fiber. The two queues are separate but they share a persistent structure. During reconciliation, updates are removed from the work-in-progress fiber, but they remain on the current fiber. That ensures that if a work-in-progress is aborted, the aborted updates are recovered by cloning from current. The work-in-progress queue is always a subset of the current queue. When the tree is committed, the work-in-progress becomes the current.
function createUpdateQueue<State>(baseState: State): UpdateQueue<State> {
const queue: UpdateQueue<State> = {
baseState,
expirationTime: NoWork,
first: null,
last: null,
callbackList: null,
hasForceUpdate: false,
isInitialized: false,
capturedValues: null,
};
return queue;
}
insertUpdateIntoQueue
export function insertUpdateIntoQueue<State>(
queue: UpdateQueue<State>,
update: Update<State>,
): void {
// Append the update to the end of the list.
if (queue.last === null) {
// Queue is empty
queue.first = queue.last = update;
} else {
queue.last.next = update;
queue.last = update;
}
if (
queue.expirationTime === NoWork ||
queue.expirationTime > update.expirationTime
) {
queue.expirationTime = update.expirationTime;
}
}