Update logic
- in
Fiber
To create aUpdate
.- Setting related Properties
lane
,eventTime
,payload
,callback
. - Function:
createUpdate
->enqueueUpdate
- Setting related Properties
- through
Scheduler
Scheduling schedules rendering.- Confirm the render task
performConcurrentWorkOnRoot
orperformSyncWorkOnRoot
The priority and execution time of. - Function:
scheduleUpdateOnFiber
->ensureRootIsScheduled
- Confirm the render task
The data structure
Class Component
UpdateQueue A circular list of Update nodes. SharedQueue is the end of the list.
type Update<State> = {
// Event time, which will be removed later, will store the transition -> event time mapping in the root directory.
eventTime: number./ / priority
lane: Lane,
// Update type
// Corresponding to UpdateState, ReplaceState, ForceUpdate, and CaptureUpdate
tag: 0 | 1 | 2 | 3.// newState or newState function
payload: any.callback: (() = > mixed) | null.// Next update
next: Update<State> | null};type UpdateQueue<State> = {
// update list
shared: SharedQueue<State>,
// The following three values are the status of the interrupted protection site.
// Update the value before interruption when interrupted by a high priority.
baseState: State,
firstBaseUpdate: Update<State> | null.lastBaseUpdate: Update<State> | null./ / side effects
effects: Array<Update<State>> | null};type SharedQueue<State> = {
// The end of the update ue is a circular linked list
pending: Update<State> | null.// merge UpdateQueue priority to UpdateQueue lanes
lanes: Lanes,
interleaved: Update<State> | null};type Fiber = {
// state holds values
memoizedState: any
// UpdateQueue
updateQueue:UpdateQueue
}
Copy the code
Function Component
The state of function components is managed by Hooks, so each Hooks has its own UpdateQueue.
type Update<S, A> = {
lane: Lane, / / priority
action: A, // newState or newState function
hasEagerState: boolean./ / state optimization
eagerState: S | null./ / state optimization
next: Update<S, A>, // Next update
};
type UpdateQueue<S, A> = {
// Update list is a circular linked list
pending: Update<S, A> | null.// merge UpdateQueue priority to UpdateQueue lanes
lanes: Lanes,
// Corresponding dispatch function
dispatch: (A= > mixed) | null.// Assign the reducer function again each time the reducer function is passed in using useReducer. It is up to date every time it is used.
lastRenderedReducer: ((S, A) = > S) | null.// The state calculated by hook was updated last time.
lastRenderedState: S | null.interleaved: Update<S, A> | null};type Hook = {
// state
memoizedState: any.// UpdateQueue
queue: any.// Next hook
next: Hook | null.// The following two values are the status of the interrupted protection site.
// Update the value before interruption when interrupted by a high priority.
baseState: any.// UpdateQueue before interruption
baseQueue: Update<any.any> | null};type Fiber = {
// state stores the end of the hook list. A circular linked list
memoizedState: Hook
// Hook has its own updateQueue,
// Fiber update ue is currently useless.
}
Copy the code
How to trigger an update
ReactDOM.render
Update has the same data structure as ClassCompoent.
updateContainer
Reactdom.render and reactdom.createroot ().render both call updateContainer.
// reactdom.render (element) and reactdom.createroot (element). Render both call updateContainer.
export function updateContainer(element: ReactNodeList, container: OpaqueRoot, parentComponent: ? React$Component<any, any>, callback: ?Function.) :Lane {
/ / current is FiberRoot
const current = container.current;
/ / time
const eventTime = requestEventTime();
/ / priority
const lane = requestUpdateLane(current);
const context = getContextForSubtree(parentComponent);
if (container.context === null) {
container.context = context;
} else {
container.pendingContext = context;
}
// Create update, ClassComponent.
const update = createUpdate(eventTime, lane);
// The FiberRoot payload is the mounted DOM node
React DevTools currently relies on this property
update.payload = {element};
callback = callback === undefined ? null : callback;
if(callback ! = =null) {
update.callback = callback;
}
// Add Update to the end of the Fiber. Update ue queue.
enqueueUpdate(current, update, lane);
// Schedule update tasks
const root = scheduleUpdateOnFiber(current, lane, eventTime);
// Scheduling priorities are related
if(root ! = =null) {
entangleTransitions(root, current, lane);
}
return lane;
}
Copy the code
this.setState — Class Component
State operations for ClassComponent are provided by a classComponentUpdater object. Instance. updater = classComponentUpdater is added to the instance when the component is created.
enqueueSetState
// this.setState actually calls the function
enqueueSetState(inst, payload, callback) {
// Get the fiber corresponding to the component
const fiber = getInstance(inst);
const eventTime = requestEventTime();
const lane = requestUpdateLane(fiber);
// Create an Update. Assign the values lane, eventTime, payload, and callback
const update = createUpdate(eventTime, lane);
update.payload = payload;
update.callback = callback;
// Add Update to the end of the Fiber. Update ue queue.
enqueueUpdate(fiber, update, lane);
// Schedule update tasks
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
// Scheduling priorities are related
if(root ! = =null) { entangleTransitions(root, fiber, lane); }}Copy the code
this.fourceUpdate — Class Component
Basically the same as this.setState. Only the UPDATE data changes slightly
// this.fourceUpdate actually calls the function
// Difference: payload cannot be passed in
enqueueForceUpdate(inst, callback) {
const fiber = getInstance(inst);
const eventTime = requestEventTime();
const lane = requestUpdateLane(fiber);
// Difference: Payload is null. (Default: null when created)
const update = createUpdate(eventTime, lane);
// Difference: update tag ForceUpdate
update.tag = ForceUpdate;
enqueueUpdate(fiber, update, lane);
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
if(root ! = =null) { entangleTransitions(root, fiber, lane); }}Copy the code
useReducer — Function Component
The new version differentiates useReducer from useState because the old version has some bugs.
dispatchReducerAction
function dispatchReducerAction<S.A> (fiber: Fiber, queue: UpdateQueue
, action: A,
,>) {
const lane = requestUpdateLane(fiber);
// update
const update: Update<S, A> = {
lane, / / priority
action,
hasEagerState: false.eagerState: null.next: (null: any),
};
// If fiber is render
// Render means that the update is triggered when the component function executes.
if (isRenderPhaseUpdate(fiber)) {
Dispatch (1) directly within the component, which is immediately triggered by component render.
//
// After the component function completes,
// Call this component function repeatedly.
// No update is triggered until the component is called.
// If too many times are triggered, an error is reported.
// The handling logic is in: function component render function renderWithHooks.
// Add Update to the end of fiber.memoizedState (Hook).queue.
enqueueRenderPhaseUpdate(queue, update);
} else {
// Add Update to the end of fiber.memoizedState (Hook).queue.
enqueueUpdate(fiber, queue, update, lane);
const eventTime = requestEventTime();
// Schedule update tasks
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
// Scheduling priorities are related
if(root ! = =null) { entangleTransitionUpdate(root, queue, lane); }}}Copy the code
useState — Function Component
The new version differentiates useReducer from useState because the old version has some bugs.
Update data structure
// eg: dispatch( n => n+1 )
const update: Update<S, A> = {
lane, / / priority
action,// dispatch Send parameters eg: n => n+1
hasEagerState: false.//
eagerState: null.//
next: (null: any),//next update
};
Copy the code
dispatchSetState
function dispatchSetState<S.A> (fiber: Fiber, queue: UpdateQueue
, action: A,
,>) {
const lane = requestUpdateLane(fiber);
const update: Update<S, A> = {
lane,
action,
hasEagerState: false.eagerState: null.next: (null: any),
};
// If fiber is render
// Render means that the update is triggered when the component function executes.
if (isRenderPhaseUpdate(fiber)) {
Dispatch (1) directly within the component, which is immediately triggered by component render.
//
// After the component function completes,
// Call this component function repeatedly.
// No update is triggered until the component is called.
// If too many times are triggered, an error is reported.
// The handling logic is in: function component render function renderWithHooks.
// Add Update to the end of fiber.memoizedState (Hook).queue.
enqueueRenderPhaseUpdate(queue, update);
} else {
// Add Update to the end of fiber.memoizedState (Hook).queue.
enqueueUpdate(fiber, queue, update, lane);
const alternate = fiber.alternate;
if (
Fiber is not updated. This is the first update in this round.
fiber.lanes === NoLanes &&
(alternate === null || alternate.lanes === NoLanes)
) {
// Calculate state directly, since it is the first update, regardless of the other logic of priority.
// This is optimized to minimize state scheduling.
const lastRenderedReducer = queue.lastRenderedReducer;
if(lastRenderedReducer ! = =null) {
let prevDispatcher;
try {
// State of the last useReducer.
const currentState: S = (queue.lastRenderedState: any);
// Calculate the state of this time
const eagerState = lastRenderedReducer(currentState, action);
// Record whether the optimization logic was executed.
update.hasEagerState = true;
update.eagerState = eagerState;
// The new state is the same as the old state.
if (is(eagerState, currentState)) { return; }}catch (error) {
// Suppress the error. It will throw again in the render phase.}}}const eventTime = requestEventTime();
// Schedule update tasks
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
// Scheduling priorities are related
if(root ! = =null) { entangleTransitionUpdate(root, queue, lane); }}}Copy the code
State update formula in concurrent mode
BaseState + Update1 + Updata2 + (Update with sufficient priority) = newState
- BaseState: after the last update
state
Value, if interrupted by a high priority, is the value calculated before the interrupt occurred. - It will filter out the low priority
Update
.
Arrange the rendering
Schedule rendering tasks based on update and react running status.
scheduleUpdateOnFiber
Schedule updates on Fiber.
export function scheduleUpdateOnFiber(
fiber: Fiber,
lane: Lane,
eventTime: number.) :FiberRoot | null {
// Merge lane into all fibers on the path from this fiber to Root.
const root = markUpdateLaneFromFiberToRoot(fiber, lane);
if (root === null) { return null; }
// The tag root has a pending update
markRootUpdated(root, lane, eventTime);
if (
// Render, and root is the same as root that is working.(executionContext & RenderContext) ! == NoLanes && root === workInProgressRoot ) {// Share the current render. The merge lane.
workInProgressRootRenderPhaseUpdatedLanes = mergeLanes(
workInProgressRootRenderPhaseUpdatedLanes,
lane,
);
} else {
// Schedule a rendering task
ensureRootIsScheduled(root, eventTime);
if (
lane === SyncLane && // Synchronization priority
executionContext === NoContext && // Execute context, no task
(fiber.mode & ConcurrentMode) === NoMode && // Not concurrent mode
) {
// Compatible with correct rendering of Leacy mode.
// if the interface is asynchronous, such as setTimeout, call setState, this logic will be used.
// Render directly.flushSyncCallbacksOnlyInLegacyMode(); }}return root;
}
Copy the code
ensureRootIsScheduled
Schedule performSyncWorkOnRoot for root (render)
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
// Save, a scheduler unit, a task created by the scheduler that is associated with scheduling in concurrent mode.
const existingCallbackNode = root.callbackNode;
// If any lane starved, marked expired.
markStarvedLanesAsExpired(root, currentTime);
// Find the lane with the highest priority
const nextLanes = getNextLanes(
root,
root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,
);
/ / no task
if (nextLanes === NoLanes) {
if(existingCallbackNode ! = =null) {
cancelCallback(existingCallbackNode);
}
root.callbackNode = null;
root.callbackPriority = NoLane;
return;
}
// Get the priority according to lane
const newCallbackPriority = getHighestPriorityLane(nextLanes);
const existingCallbackPriority = root.callbackPriority;
if (
// Check if there is an ongoing task, reuse it with the same priority.
existingCallbackPriority === newCallbackPriority &&
) {
return;
}
if(existingCallbackNode ! =null) {
// Cancel the existing callback. We will arrange a new one below.
cancelCallback(existingCallbackNode);
}
// Schedule a new callback.
let newCallbackNode;
// Synchronization priority
if (newCallbackPriority === SyncLane) {
// Place performSyncWorkOnRoot in the sync execution callback queue SyncCallbacks
if (root.tag === LegacyRoot) {
// In Legacy mode, schedule rendering performSyncWorkOnRoot
scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
} else {
// In concurrent mode, schedule rendering performSyncWorkOnRoot
scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
}
// Whether microtasks are supported
// The current synchronization priority is performed immediately
if (supportsMicrotasks) {
FlushSyncCallbacks: immediately execute performSyncWorkOnRoot
scheduleMicrotask(flushSyncCallbacks);
} else {
FlushSyncCallbacks: immediately execute performSyncWorkOnRoot
scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);
}
newCallbackNode = null;
} else {
// Asynchronous priority in concurrent mode.
let schedulerPriorityLevel;
// lane => scheduler Priority
switch (lanesToEventPriority(nextLanes)) {
case DiscreteEventPriority:
schedulerPriorityLevel = ImmediateSchedulerPriority;
break;
case ContinuousEventPriority:
schedulerPriorityLevel = UserBlockingSchedulerPriority;
break;
case DefaultEventPriority:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
case IdleEventPriority:
schedulerPriorityLevel = IdleSchedulerPriority;
break;
default:
schedulerPriorityLevel = NormalSchedulerPriority;
break;
}
// Saves tasks created by the Scheduler
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root),
);
}
// Save, schedule priority
root.callbackPriority = newCallbackPriority;
// Saves tasks created by the Scheduler
root.callbackNode = newCallbackNode;
}
Copy the code
performSyncWorkOnRoot
This function will diff and render the DOM. This article does not go into details about how to render.
review
Writing time: 2021-01-16 React Version: 17.0.3