React source series
- Create the Fiber structure for React source parsing
- React Fiber (1)
- React code parsing of Fiber (2) beginWork
We got the fiberRoot structure from the React Fiber Structure creation, and this post will focus on how to render the fiberRoot into the view.
How do I generate the workInProgress structure
The previous part generated a fiberRoot by looking at the call stack and started parsing the fiberRoot structure by calling the updateContainer function as the entry function.
updateContainer(children, fiberRoot, parentComponent, callback);
Copy the code
The parentComponent is the container (div#root) in react.render.
UpdateContainer handles several things.
1. Obtain the current prioritylane
(lane)
const current = container.current;//container - fiber root
// Get the priority lane
const lane = requestUpdateLane(current);
Copy the code
Lane is 1.
After React V17, lane is the priority. The smaller the lane value, the higher the priority. Lane is stored in binary, with 31 bits in total, and each bit is a lane. Click here to see the source code.
export const TotalLanes = 31;
export const NoLanes: Lanes = / * * / 0b0000000000000000000000000000000;
export const NoLane: Lane = / * * / 0b0000000000000000000000000000000;
export const SyncLane: Lane = / * * / 0b0000000000000000000000000000001;
// The input box value
const InputContinuousHydrationLane: Lane = / * * / 0b0000000000000000000000000000010;
export const InputContinuousLane: Lanes = / * * / 0b0000000000000000000000000000100;
export const DefaultHydrationLane: Lane = / * * / 0b0000000000000000000000000001000;
export const DefaultLane: Lanes = / * * / 0b0000000000000000000000000010000;
Copy the code
In requestUpdateLane, when the render is initialized, lane is 1.
export function requestUpdateLane(fiber: Fiber) :Lane {
// Special cases
const mode = fiber.mode;
if ((mode & ConcurrentMode) === NoMode) {
// 0b0000000000000000000000000000001;
// Lane 1 is priority 1
return (SyncLane: Lane);
/ /...
Copy the code
In the React source code, there is a separate file that holds the binary values for the different types of prioritized lanes. There’s a separate article on Lane that explains what it means, and how to use lane for permission design, but for now let’s just know that this place gets priority for Lane as a fiber.
Create context.
During initialization, the function getContextForSubtree is used to determine whether parentComponent exists. ParentComponet is the parent element of root and null at this time. Otherwise, parentContext is returned.
function getContextForSubtree(parentComponent: ? React$Component
,
,>) :Object {
if(! parentComponent) {return emptyContextObject;
}
const fiber = getInstance(parentComponent);
const parentContext = findCurrentUnmaskedContext(fiber);
if (fiber.tag === ClassComponent) {
const Component = fiber.type;
if (isLegacyContextProvider(Component)) {
returnprocessChildContext(fiber, Component, parentContext); }}return parentContext;
}
Copy the code
Create an UPDATE
Create an update object for enqueueUpdate.
// Create an update object
const update = createUpdate(eventTime, lane);
// ...
export function createUpdate(eventTime: number, lane: Lane) :Update< * >{
const update: Update<*> = {
eventTime,
lane,
tag: UpdateState,/ / tag to 0 | 2 | 3 | 1
payload: null.callback: null.next: null};return update;
}
// ...
update.payload = { element };
callback = callback === undefined ? null : callback;
update.callback = callback;
}
// Update the queue
enqueueUpdate(current, update, lane);
Copy the code
EnqueueUpdate is used to add a sharedQueue, sharedQueue, that can be shared with workInProgress and FiberRoot.
export function enqueueUpdate<State> (
fiber: Fiber,//fiberRoot
update: Update<State>,
lane: Lane,
) {
const updateQueue = fiber.updateQueue;
if (updateQueue === null) {
// Only occurs if the fiber has been unmounted.
return;
}
// The current queue and the cache queue share a persistent queue
const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
// Compare fiber lane and lane, and update the same
// Render is not initialized
if (isInterleavedUpdate(fiber, lane)) {
const interleaved = sharedQueue.interleaved;// Interleaved updates
if (interleaved === null) {
// If this is the first update, create a bidirectional list
update.next = update;
// Interleaved updates to this queue will be displayed when the current render ends
// Is transferred to the suspended queue.
pushInterleavedQueue(sharedQueue);
} else {
// interleaved.next -> update.next update - interleaved.next;
// interleaved.next = update
// update.next = interleaved.next = update
update.next = interleaved.next;
interleaved.next = update;
}
sharedQueue.interleaved = update;
} else {
const pending = sharedQueue.pending;
if (pending === null) {
// This is the first update. Create a unidirectional linked list.
update.next = update;
} else {
// Define a bidirectional listupdate.next = pending.next; pending.next = update; } sharedQueue.pending = update; }}Copy the code
Fourth, callscheduleUpdateOnFiber
In this case, we only look at the logic that the render function executes. When the page is initialized, only fiberRoot is generated, not workInProgressRoot, which is null. Therefore, It’s going to do a little bit of logic, call performSyncWorkOnRoot.
export function scheduleUpdateOnFiber(
fiber: Fiber,
lane: Lane,// Update lane currently
eventTime: number,
) :FiberRoot | null {...// Mark root as having pending updates.
markRootUpdated(root, lane, eventTime);
if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
...
}
/ / root for fiber, workInfoProGressRoot is null, to false
if (root === workInProgressRoot) {
...
}
if (lane === SyncLane) {
if (
// Check whether we are on unbatchedUpdates(executionContext & LegacyUnbatchedContext) ! == NoContext &&// Check if we haven't rendered yet
(executionContext & (RenderContext | CommitContext)) === NoContext
) {
// Register pending interactions at the root directory to avoid losing traced interaction data.
schedulePendingInteractions(root, lane);
performSyncWorkOnRoot(root);
} else {
ensureRootIsScheduled(root, eventTime);
schedulePendingInteractions(root, lane);
if (
executionContext === NoContext &&
(fiber.mode & ConcurrentMode) === NoMode
) {
/ / resetresetRenderTimer(); flushSyncCallbackQueue(); }}}else {
/ /...
ensureRootIsScheduled(root, eventTime);
schedulePendingInteractions(root, lane);
}
return root;
}
Copy the code
PerformSyncWorkOnRoot refreshes Effects and synchronizes the render Fiber.
/ /...
if (
root === workInProgressRoot &&
areLanesExpired(root, workInProgressRootRenderLanes)
) {
lanes = workInProgressRootRenderLanes;
exitStatus = renderRootSync(root, lanes);
} else {
lanes = getNextLanes(root, NoLanes);
// Render root synchronously
exitStatus = renderRootSync(root, lanes);
}
/ /...
Copy the code
The renderRootSync function first creates a bidirectional list tree of workInProgress associated through alternate and fiberRoot
.if(workInProgressRoot ! == root || workInProgressRootRenderLanes ! == lanes) {// Create a tree for workInProgress
prepareFreshStack(root, lanes);
/ / handle hang interaction, and there will be interactive, binding to root in the store. The memoizedInteractionsstartWorkOnPendingInteractions(root, lanes); }...// Create workInProgress and workInProgressRoot.
function prepareFreshStack(root: FiberRoot, lanes: Lanes) {
root.finishedWork = null;
root.finishedLanes = NoLanes;
const timeoutHandle = root.timeoutHandle;
if(timeoutHandle ! == noTimeout) {// The root previous suspended and scheduled a timeout to commit a fallback
// state. Now that we have additional work, cancel the timeout.
root.timeoutHandle = noTimeout;
// $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
cancelTimeout(timeoutHandle);
}
if(workInProgress ! = =null) {
let interruptedWork = workInProgress.return;
while(interruptedWork ! = =null) {
unwindInterruptedWork(interruptedWork, workInProgressRootRenderLanes);
interruptedWork = interruptedWork.return;
}
}
workInProgressRoot = root;
workInProgress = createWorkInProgress(root.current, null);// Create workInProgess based on the current node
workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;
workInProgressRootExitStatus = RootIncomplete;
workInProgressRootFatalError = null; workInProgressRootSkippedLanes = NoLanes; workInProgressRootUpdatedLanes = NoLanes; workInProgressRootPingedLanes = NoLanes; . }Copy the code
Execute the subsequent code, call the workLoopSync function, start to process the workInProgress two-way linked list, and enter the beginWork phase.
do {
try {
workLoopSync();// Update synchronously
break;
} catch(thrownValue) { handleError(root, thrownValue); }}while (true); . .function workLoopSync() {
// Already timed out, so perform work without checking if we need to yield.
while(workInProgress ! = =null) {
performUnitOfWork(workInProgress);// Execute the unit of work}}...Copy the code
At this time, the workInProgress structure has been generated, and its structure is consistent with the left structure. I omitted the drawing here and connected through the alternate in the middle. Figure is as follows: