Photo credit: unsplash.com/photos/kLfk…
preface
Currently React has three modes:
- legacyPattern: ReactDOM. Render (
element
The rootNode). This is how the React App is currently used. There are currently no plans to remove this schema, but it may not support these new features. - blockingPattern: ReactDOM createBlockingRoot (rootNode.) render (
element
). It is currently being tested. As the first step in migrating to concurrent mode. - concurrentPattern: ReactDOM createRoot (rootNode.) render (
element
). It is currently being tested and is intended to be the default development mode for React when it is stable in the future. This mode turns on all the new features.
The React of the render phase began in performSyncWorkOnRoot or performConcurrentWorkOnRoot method calls, it mainly depends on the updated is synchronous or asynchronous update.
performSyncWorkOnRoot & performConcurrentWorkOnRoot
- performSyncWorkOnRoot
// performSyncWorkOnRoot calls this method
// The work loop is an extremely hot path. Tell Closure not to inline it.
/ * *@noinline * /
function workLoopSync() {
// Already timed out, so perform work without checking if we need to yield.
while(workInProgress ! = =null) { performUnitOfWork(workInProgress); }}Copy the code
You can see the source code here
- performConcurrentWorkOnRoot
/ / performConcurrentWorkOnRoot will invoke this method
/ * *@noinline * /
function workLoopConcurrent() {
// Perform work until Scheduler asks us to yield
while(workInProgress ! = =null&&! shouldYield()) { performUnitOfWork(workInProgress); }}Copy the code
You can see the source code here
The only difference between the two modes is whether to call shouldYield. If the current browser frame has no time left, shouldYield stops the loop until the browser has idle time before continuing the loop.
WorkInProgress represents the currently created workInProgress Fiber.
The performUnitOfWork method creates the next Fiber node and assigns it to workInProgress, which is connected to the created Fiber node to form a Fiber tree.
React Fiber Reconciler is reconstructed from the Stack Reconciler and realizes interruptible recursion through traversal. Therefore, the work of performUnitOfWork can be divided into two parts: “recursion” and “return”.
The “pass” stage
The depth-first traversal starts from the root node rootFiber of the component tree. Call the beginWork method for each Fiber node traversed.
This method creates a sub-fiber node based on the incoming Fiber node and connects the two Fiber nodes.
The “homing” phase is entered when the leaf node (that is, the component with no child components) is traversed.
The “return” stage
CompleteWork is called during the homing phase to process the Fiber node.
When a Fiber node completes completeWork, if it has a sibling Fiber node (i.e. Fiber.sibling! == null), it enters the “pass” phase of its sibling Fiber.
If there is no sibling Fiber, the parent Fiber will go through the “home” phase.
The “recursion” and “return” phases are interlaced until the “return” to rootFiber. At this point, the work of the Render phase is over.
For example,
function App() {
return (
<div className="App">Learning the React<span>The source code</span>
</div>
);
}
export default App;
ReactDOM.render(<App />.document.getElementById('root'));
Copy the code
The corresponding Fiber tree structure
The Render phase executes in sequence:
- rootFiber beginWork
- App Fiber beginWork
- div Fiber beginWork
- Learning the React beginWork
- Learning the React
completeWork
- span Fiber beginWork
- span Fiber
completeWork
- div Fiber
completeWork
- App Fiber
completeWork
- rootFiber
completeWork
The reason why there is no beginWork/completeWork of “source code” Fiber node is that as a means of performance optimization, React will deal with the Fiber with only a single text child node specially.
beginWork
You can see the beginWork source code here
BeginWork’s job is to pass in the current Fiber node and create sub-fiber nodes
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) :Fiber | null {
/ /... Elliptic function body
}
Copy the code
- Current: Fiber node at the last update of the Fiber node corresponding to the current component, that is, workinprogress.alternate;
- WorkInProgress: Fiber node corresponding to the current component;
- RenderLanes: priority related.
In the React dual cache mechanism, except for the rootFiber(the root node of the application), when the component is mounted, there is no Fiber node corresponding to the current component in the last update, because it is the first rendering. That is, mount current === null.
When the component update is updated, the current! = = null.
For this reason, beginWork’s work can be divided into two parts:
- When mounting components: Except
rootFiber
Current === null. Different types of subfiber nodes are created depending on the fiber.tag; - Update: If
current
The current node can be reused when certain conditions are met, so that the current. Child can be cloned as workinprogress. child without creating a new workinprogress. child.
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes
) :Fiber | null {
// Update: if current has a possible optimization path, reuse current (i.e. last updated Fiber node)
if(current ! = =null) {
/ /... Omit code
/ / reuse current
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderLanes,
);
} else {
didReceiveUpdate = false;
}
// Mount: create different subfiber nodes depending on the tag
switch (workInProgress.tag) {
case IndeterminateComponent:
/ /... omit
case LazyComponent:
/ /... omit
case FunctionComponent:
/ /... omit
case ClassComponent:
/ /... omit
case HostRoot:
/ /... omit
case HostComponent:
/ /... omit
case HostText:
/ /... omit
case SuspenseComponent:
/ /... omit
/ /... Omit other types}}Copy the code
When the update
DidReceiveUpdate === false (reuse the previous subfiber, no need to create new subfiber)
oldProps === newProps && workInProgress.type === current.type
, props and fiber.type remain the same;! includesSomeLane(renderLanes, updateLanes)
, that is, the priority of the current Fiber node is insufficient;
if(current ! = =null) {
// Omit the code...
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if( oldProps ! == newProps || hasLegacyContextChanged() || (__DEV__ ? workInProgress.type ! == current.type :false)
) {
didReceiveUpdate = true;
} else if(! includesSomeLane(renderLanes, updateLanes)) { didReceiveUpdate =false;
switch (workInProgress.tag) {
// Omit the code...
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
} else {
if((current.flags & ForceUpdateForLegacySuspense) ! == NoFlags) { didReceiveUpdate =true;
} else {
didReceiveUpdate = false; }}}else {
didReceiveUpdate = false;
}
Copy the code
When the mount
When the optimization path is not satisfied, we enter the second part and create a new subfiber. Depending on the fiber.tag, enter the logic for creating different types of fibers.
You can see the component type for the tag here
// Mount: create different subfiber nodes depending on the tag
switch (workInProgress.tag) {
case IndeterminateComponent:
/ /... omit
case LazyComponent:
/ /... omit
case FunctionComponent:
/ /... omit
case ClassComponent:
/ /... omit
case HostRoot:
/ /... omit
case HostComponent:
/ /... omit
case HostText:
/ /... omit
case SuspenseComponent:
/ /... omit
/ /... Omit other types
}
Copy the code
reconcileChildren
ReconcileChildren is the core part of the Reconciler module.
-
For components at mount, it creates a new subfiber node;
-
For an updated component, it compares the current component to the Fiber node corresponding to the component last updated (known as the Diff algorithm) and generates a new Fiber node from the result
export function reconcileChildren(
current: Fiber | null,
workInProgress: Fiber,
nextChildren: any,
renderLanes: Lanes,
) {
if (current === null) {
workInProgress.child = mountChildFibers(
workInProgress,
null,
nextChildren,
renderLanes,
);
} else{ workInProgress.child = reconcileChildFibers( workInProgress, current.child, nextChildren, renderLanes, ); }}Copy the code
You can see the reconcileChildren source code here
Current === null? To distinguish the component mount phase from the Update phase. Finally, it will generate a new child Fiber node and assign the value to workinProgress. child as the return value of this beginWork.
The difference between reconcileChildFibers and mountChildFibers is that the passed Boolean values are different.
export const reconcileChildFibers = ChildReconciler(true);
export const mountChildFibers = ChildReconciler(false);
Copy the code
You can see the source code here
The ChildReconciler method primarily determines whether side effects are tracked.
function ChildReconciler(shouldTrackSideEffects) {
function deleteChild(returnFiber: Fiber, childToDelete: Fiber) :void {
if(! shouldTrackSideEffects) {// Noop.
return;
}
const deletions = returnFiber.deletions;
if (deletions === null) {
returnFiber.deletions = [childToDelete];
returnFiber.flags |= ChildDeletion;/ / tag effectTag
} else{ deletions.push(childToDelete); }}... Omit code}Copy the code
You can see ChildReconciler in the source code here
effectTag
The work of the Render phase is done in memory, and the Renderer is notified of the DOM operations that need to be performed when the work is done. The specific types of DOM operations to be performed are stored in fiber.effectTag.
Here you can see the DOM operation that corresponds to the effectTag
// The DOM needs to be inserted into the page
export const Placement = / * * / 0b00000000000010;
// DOM needs to be updated
export const Update = / * * / 0b00000000000100;
// The DOM needs to be inserted into the page and updated
export const PlacementAndUpdate = / * * / 0b00000000000110;
// The DOM needs to be deleted
export const Deletion = / * * / 0b00000000001000; .Copy the code
When the Renderer phase inserts the DOM node corresponding to the Fiber node into the page, two conditions must be met:
-
Fiber. StateNode exists, that is, the corresponding DOM node is stored in the fiber node.
-
Placement effectTag exists in Fiber node;
StateNode will be created in completeWork. Only rootFiber(with current) will be assigned Placement effectTags on mount, and only one insert will be performed on commit.
The flow chart of beginWork
completeWork
You can see the source code for completeWork here
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) :Fiber | null {
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
case IndeterminateComponent:
case LazyComponent:
case SimpleMemoComponent:
case FunctionComponent:
case ForwardRef:
case Fragment:
case Mode:
case Profiler:
case ContextConsumer:
case MemoComponent:
return null;
case ClassComponent: {
/ /... omit
return null;
}
case HostRoot: {
/ /... omit
updateHostContainer(workInProgress);
return null;
}
case HostComponent: {
/ /... omit
return null;
}
/ /... omit
Copy the code
We focus on the HostComponent (the Fiber node for the native DOM component) that is required for page rendering.
HostComponent
case HostComponent: {
popHostContext(workInProgress);
const rootContainerInstance = getRootHostContainer();
const type = workInProgress.type;
if(current ! = =null&& workInProgress.stateNode ! =null) {
updateHostComponent(
current,
workInProgress,
type,
newProps,
rootContainerInstance,
);
// Update the case
/ /... omit
} else {
// Mount situation
/ /... omit
}
return null;
}
Copy the code
Workinprogress. stateNode represents the DOM node corresponding to the Fiber node.
When the component update
When you update, the Fiber node already has a DOM node, so you don’t need to generate a DOM node. The main thing to do is to handle props
if(current ! = =null&& workInProgress.stateNode ! =null) {
// Component update
updateHostComponent(
current,
workInProgress,
type,
newProps,
rootContainerInstance,
);
if (current.ref !== workInProgress.ref) {
markRef(workInProgress);
}
}
Copy the code
You can see the updateHostComponent method definition here.
Inside the updateHostComponent, be processed props will be assigned to workInProgress. UpdateQueue, and eventually will be rendered on the page in the commit phase.
updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
var oldProps = current.memoizedProps;
if (oldProps === newProps) {
return;
}
var instance = workInProgress.stateNode;
var currentHostContext = getHostContext();
var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext);
workInProgress.updateQueue = updatePayload;
if(updatePayload) { markUpdate(workInProgress); }};Copy the code
UpdatePayload is in the form of an array. The value of its even-numbered index is the changed prop key, and the value of its odd index is the changed Prop value.
When the component is the mount
The main logic for mounting consists of three:
Generate the corresponding DOM node for the Fiber node
;Insert the descendant DOM node into the newly generated DOM node
;Processes props similar to updateHostComponent in update logic
;
const currentHostContext = getHostContext();
if(wasHydrated){
...
}else {
// Create a DOM node for fiber
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
// Insert the descendant DOM node into the newly generated DOM node
appendAllChildren(instance, workInProgress, false.false);
// The DOM node is assigned to fiber.statenode
workInProgress.stateNode = instance;
UpdateHostComponent = props; // updateHostComponent = props
if( finalizeInitialChildren( instance, type, newProps, rootContainerInstance, currentHostContext, ) ) { markUpdate(workInProgress); }}Copy the code
The Placement effectTag will only exist in the rootFiber when mount. So how does the COMMIT phase insert the entire DOM tree into the page with a single DOM insert (corresponding to a Placement effectTag)?
The reason is the appendAllChildren method in completeWork.
Since completeWork is a function called in the “home” phase, each call to appendAllChildren inserts the generated descendant DOM node under the currently generated DOM node. So when we “revert” to rootFiber, we already have a constructed off-screen DOM tree.
effectList
As a basis for DOM operations, you need to find all Fiber nodes with effecttags in the commit phase and execute the corresponding effectTag operations in turn. If you go through the Fiber tree again during the commit phase and look for the effectTag! Fiber nodes with == NULL are obviously inefficient.
To solve this problem, in the upper function of completeWork, completeUnitOfWork, Each Fiber node that completes the completeWork and has an effectTag is stored in a unidirectional linked list called an effectList.
The first Fiber node in the effectList is stored in fiber.firsteffect, and the last element is stored in fiber.lasteffect.
Similar to appendAllChildren, during the “homing” phase, all Fiber nodes with effecttags are appented to the effectList, resulting in a unidirectional linked list starting with Rootfiber.firsteffect.
nextEffect nextEffect
rootFiber.firstEffect -----------> fiber -----------> fiber
Copy the code
In the COMMIT phase, all effects are performed by simply traversing the effectList.
You can see the completeUnitOfWork code logic here.
Process the end
At this point, the render phase is complete. In the performSyncWorkOnRoot function, fiberRootNode is passed to the commitRoot method to start the commit process.
const finishedWork: Fiber = (root.current.alternate: any);
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
commitRoot(root);
Copy the code
The flow chart of completeWork
The entire rendering process
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
num: 0}; }updateNum() {
const { num } = this.state;
this.setState({ num: num + 1 });
}
render() {
return (
<div className="App">
<div onClick={this.updateNum.bind(this)}>Learn the React - num: {this. State. Num}</div>
<span>The source code</span>
</div>
);
}
}
ReactDOM.render(<App />.document.getElementById('root'));
Copy the code
Take the class component above as an example
-
rootFiber beginWork
-
App Fiber beginWork
-
Div Fiber beginWork with className “App”
-
Div Fiber beginWork with onClick event
-
“Learn React–num:” Fiber beginWork
-
Learn React–num: Fiber completeWork
-
Num is 0 Fiber beginWork
-
Num is 0 Fiber completeWork
-
Div Fiber completeWork for onClick event exists
-
span Fiber beginWork
-
span Fiber
completeWork
-
Div Fiber completeWork with className “App”
-
App Fiber
completeWork
-
rootFiber
completeWork
Component Mount phase
We know that the Render phase will first execute the beginWork method, which will only return a child fiber node. Take the above test Demo as an example to see the main process.
First, beginWork is the root node rootFiber of the current application. According to the principle of fiber dual caching, there are current Fiber and workInProgress of HostRoot with tag 3.
The second beginWork is App ClassComponent with tag 1. It has no current fiber but only workInProgress fiber.
The third element to enter beginWork is div HostComponent with tag of 5. It also has no current fiber but only workInProgress fiber.
The rest of the ellipsis…..
In order todiv
Take the example of HostComponent, and look at the main process of its mount phase.
beginWork
First, enter beginWork and enter updateHostComponent according to workinprogress. tag;
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) :Fiber | null {...switch (workInProgress.tag) {
.....
case HostComponent:
returnupdateHostComponent(current, workInProgress, renderLanes); . }... }Copy the code
In the updateHostComponent method,var isDirectTextChild = shouldSetTextContent(type, nextProps);
Is to determine if the current fiber has a unique text child node, if so, it will not create a child fiber node for it.It's an optimization
.
function updateHostComponent(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
pushHostContext(workInProgress);
if (current === null) {
tryToClaimNextHydratableInstance(workInProgress);
}
const type = workInProgress.type;
const nextProps = workInProgress.pendingProps;
constprevProps = current ! = =null ? current.memoizedProps : null;
let nextChildren = nextProps.children;
const isDirectTextChild = shouldSetTextContent(type, nextProps);
if (isDirectTextChild) {
nextChildren = null;
} else if(prevProps ! = =null && shouldSetTextContent(type, prevProps)) {
workInProgress.flags |= ContentReset;
}
markRef(current, workInProgress);
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
Copy the code
UpdateHostComponent is calledreconcileChildren
Method creates a subfiber node for the current workInProgress.
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
Copy the code
In the reconcileChildren method, according tocurrent fiber
Whether there is a different processing logic to enter. There is no current fiber in the mount phase, so entermountChildFibers
Methods.
export function reconcileChildren(
current: Fiber | null,
workInProgress: Fiber,
nextChildren: any,
renderLanes: Lanes,
) {
if (current === null) {
workInProgress.child = mountChildFibers(
workInProgress,
null,
nextChildren,
renderLanes,
);
} else{ workInProgress.child = reconcileChildFibers( workInProgress, current.child, nextChildren, renderLanes, ); }}Copy the code
export const reconcileChildFibers = ChildReconciler(true);
export const mountChildFibers = ChildReconciler(false);
Copy the code
MountChildFibers and reconcileChildFibers are both delivered to different Boolean values via the ChildReconciler method.
The ChildReconciler method returns the reconcileChildFibers method.
function ChildReconciler(shouldTrackSideEffects) {
function deleteChild(returnFiber: Fiber, childToDelete: Fiber) :void {
if(! shouldTrackSideEffects) {// Noop.
return;
}
const deletions = returnFiber.deletions;
if (deletions === null) {
returnFiber.deletions = [childToDelete];
returnFiber.flags |= ChildDeletion;
} else{ deletions.push(childToDelete); }}...return reconcileChildFibers;
}
Copy the code
ShouldTrackSideEffects says whether to track side effects, returns directly if not, and marks it if it does.
reconcileChildFibers
In reconcileChildFibers method, different processing logic is entered by judging different types of newChild. If newChild is an array, it enters the processing logic of reconcileChildrenArray.
function reconcileChildFibers(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChild: any,
lanes: Lanes,
) :Fiber | null {
const isUnkeyedTopLevelFragment =
typeof newChild === 'object'&& newChild ! = =null &&
newChild.type === REACT_FRAGMENT_TYPE &&
newChild.key === null;
if (isUnkeyedTopLevelFragment) {
newChild = newChild.props.children;
}
if (typeof newChild === 'object'&& newChild ! = =null) {...if (isArray(newChild)) {
returnreconcileChildrenArray( returnFiber, currentFirstChild, newChild, lanes, ); }...return deleteRemainingChildren(returnFiber, currentFirstChild);
}
Copy the code
reconcileChildrenArray
The newFiber node is created in the reconcileChildrenArray method, but only one is created.
We know from the figure above that the createChild method is called.
But only one fiber node will be created
createChild
The createChild method enters different processing logic depending on the type of newChild.
createFiberFromElement
CreateFiberFromElement method will be called createFiberFromTypeAndProps returns and fiber.
export function createFiberFromElement(element: ReactElement, mode: TypeOfMode, lanes: Lanes,) :Fiber {
let owner = null;
const type = element.type;
const key = element.key;
const pendingProps = element.props;
const fiber = createFiberFromTypeAndProps(
type,
key,
pendingProps,
owner,
mode,
lanes,
);
return fiber;
}
Copy the code
createFiberFromTypeAndProps
The createFiber method is then called to create the fiber node.
createFiber
var createFiber = function (tag, pendingProps, key, mode) {
return new FiberNode(tag, pendingProps, key, mode);
};
Copy the code
FiberNode
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// Instance
this.tag = tag;
this.key = key;
this.elementType = null;
this.type = null;
this.stateNode = null;
// Fiber
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
this.ref = null;
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// Effects
this.flags = NoFlags;
this.subtreeFlags = NoFlags;
this.deletions = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null;
}
Copy the code
The call stack for the “pass” phase
completeWork
< span style = “font-size: 16px;” The second entry into the completeWork, also with tag 6, is the “0” text fiber node; The third entry to completeWork is a HostComponent with tag 5, elementType “div” and an onClick event. Let’s use this fiber node as an example.
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) :Fiber | null {
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
....
case HostComponent: {
popHostContext(workInProgress);
const rootContainerInstance = getRootHostContainer();
const type = workInProgress.type;
if(current ! = =null&& workInProgress.stateNode ! =null) {
updateHostComponent(
current,
workInProgress,
type,
newProps,
rootContainerInstance,
);
if (current.ref !== workInProgress.ref) {
markRef(workInProgress);
}
} else {
if(! newProps) { bubbleProperties(workInProgress);return null;
}
const currentHostContext = getHostContext();
if(wasHydrated) { ..... }}else {
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
appendAllChildren(instance, workInProgress, false.false);
workInProgress.stateNode = instance;
if( finalizeInitialChildren( instance, type, newProps, rootContainerInstance, currentHostContext, ) ) { markUpdate(workInProgress); }}if(workInProgress.ref ! = =null) {
markRef(workInProgress);
}
}
bubbleProperties(workInProgress);
return null; }... }}Copy the code
createInstance
The createInstance method creates the corresponding DOM node.
const instance = createInstance(type,newProps,rootContainerInstance,
currentHostContext,
workInProgress);
Copy the code
createElement
The createElement method is called in the createInstance method to create the DOM node.
function createElement(type, props, rootContainerElement, parentNamespace) {
var isCustomComponentTag;
var ownerDocument = getOwnerDocumentFromRootContainer(rootContainerElement);
var domElement;
var namespaceURI = parentNamespace;
if (namespaceURI === HTML_NAMESPACE) {
namespaceURI = getIntrinsicNamespace(type);
}
if (namespaceURI === HTML_NAMESPACE) {
{
isCustomComponentTag = isCustomComponent(type, props); // Should this check be gated by parent namespace? Not sure we want to
// allow <SVG> or <mATH>.
if(! isCustomComponentTag && type ! == type.toLowerCase()) { error('<%s /> is using incorrect casing. ' + 'Use PascalCase for React components, ' + 'or lowercase for HTML elements.', type); }}if (type === 'script') {
// Create the script via .innerHTML so its "parser-inserted" flag is
// set to true and it does not execute
var div = ownerDocument.createElement('div');
div.innerHTML = '<script><' + '/script>'; // eslint-disable-line
// This is guaranteed to yield a script element.
var firstChild = div.firstChild;
domElement = div.removeChild(firstChild);
} else if (typeof props.is === 'string') {
// $FlowIssue `createElement` should be updated for Web Components
domElement = ownerDocument.createElement(type, {
is: props.is
});
} else {
domElement = ownerDocument.createElement(type);
if (type === 'select') {
var node = domElement;
if (props.multiple) {
node.multiple = true;
} else if(props.size) { node.size = props.size; }}}}else {
domElement = ownerDocument.createElementNS(namespaceURI, type);
}
{
if (namespaceURI === HTML_NAMESPACE) {
if(! isCustomComponentTag &&Object.prototype.toString.call(domElement) === '[object HTMLUnknownElement]' && !hasOwnProperty.call(warnedUnknownTags, type)) {
warnedUnknownTags[type] = true;
error('The tag <%s> is unrecognized in this browser. ' + 'If you meant to render a React component, start its name with ' + 'an uppercase letter.', type); }}}return domElement;
}
Copy the code
appendAllChildren
The appendAllChildren method inserts the created DOM into the previously created DOM tree.
var instance = createInstance(type, newProps, rootContainerInstance, currentHostContext, workInProgress);
appendAllChildren(instance, workInProgress, false.false);
workInProgress.stateNode = instance;
if (finalizeInitialChildren(instance, type, newProps, rootContainerInstance)) {
markUpdate(workInProgress);
}
Copy the code
workInProgress.stateNode = instance;
Assign the created DOM node to the stateNode of the workInProgress fiber.
finalizeInitialChildren
The finalizeInitialChildren method sets the properties for the DOM node.
performSyncWorkOnRoot
When all nodes complete the completeWork, the other logic of performSyncWorkOnRoot is entered.
function performSyncWorkOnRoot(root) {...var finishedWork = root.current.alternate;
root.finishedWork = finishedWork;
root.finishedLanes = lanes;
commitRoot(root); // Enter the COMMIT phase
ensureRootIsScheduled(root, now());
return null;
}
Copy the code
Root.current. alternate points to the workInProgress Fiber node created by the current update.
Component Update phase
When we click on the div tag to change the value of Num, the component enters the update phase.
div
beginWork
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) :Fiber | null {...if(current ! = =null) {
var oldProps = current.memoizedProps;
var newProps = workInProgress.pendingProps;
if(oldProps ! == newProps || hasContextChanged() || ( workInProgress.type ! == current.type )) { didReceiveUpdate =true; }... }else {
didReceiveUpdate = false;
}
workInProgress.lanes = NoLanes;
switch (workInProgress.tag) {
...
case HostComponent:
returnupdateHostComponent(current, workInProgress, renderLanes); . }... }Copy the code
Current Fiber exists at this point in the component update phase, and the didReceiveUpdate = true condition is set, followed by the updateHostComponent logic.
updateHostComponent
function updateHostComponent(current, workInProgress, renderLanes) {... reconcileChildren(current, workInProgress, nextChildren, renderLanes);return workInProgress.child;
}
Copy the code
The updateHostComponent method reconcileChildren is invoked;
reconcileChildren
The reconcileChildren method compares current fiber and nextChildren, and creates a new fiber node and returns the result of the comparison.
completeWork
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) :Fiber | null {
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
...
case HostComponent: {
....
if(current ! = =null&& workInProgress.stateNode ! =null) {
updateHostComponent$1( current, workInProgress, type, newProps, rootContainerInstance, ); . }... }... }Copy the code
updateHostComponent$1
updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {...var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext);
workInProgress.updateQueue = updatePayload;
if(updatePayload) { markUpdate(workInProgress); }};Copy the code
The most important method in the updateHostComponent$1 method is the prepareUpdate method, which is concerned with property changes.
prepareUpdate
function prepareUpdate(domElement, type, oldProps, newProps, rootContainerInstance, hostContext) {{...return diffProperties(domElement, type, oldProps, newProps);
}
Copy the code
diffProperties
function diffProperties(domElement, tag, lastRawProps, nextRawProps, rootContainerElement) {...return updatePayload;
}
Copy the code
Return to updatePayload diffProperties method finally, and then assigned to workInProgress. UpdateQueue.
From here, the main update process in the Render phase is over.
The resources
React Technology revealed