When an update is generated, the Fiber node of workInProgressTree is generated in two ways:
re-render
- reuse
currentTree
的Fiber
node
The following research was conducted in this paper:
Can you reuse the Fiber node of currentTree when updating?
beginWork
When scheduling update, it will enter the render stage, which is the stage of Fiber generation, at this time, the beginWork method will be called, in which the processing of class components and function components is as follows:
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
){
switch (workInProgress.tag) {
case FunctionComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateFunctionComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
case ClassComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
returnupdateClassComponent( current, workInProgress, Component, resolvedProps, renderLanes, ); }}}Copy the code
Let’s look at ClassComponent generating Fiber first and then FunctionComponent.
ClassComponent
function updateClassComponent(
current: Fiber | null,
workInProgress: Fiber,
Component: any,
nextProps: any,
renderLanes: Lanes,
) {
shouldUpdate = updateClassInstance(
current,
workInProgress,
Component,
nextProps,
renderLanes,
);
const nextUnitOfWork = finishClassComponent(
current,
workInProgress,
Component,
shouldUpdate,
hasContext,
renderLanes,
);
return nextUnitOfWork;
}
Copy the code
The finishClassComponent method returns the Fiber node, and whether current Fiber can be reused depends on the updateClassInstance method.
Take a look at updateClassInstance:
function updateClassInstance(current: Fiber, workInProgress: Fiber, ctor: any, newProps: any, renderLanes: Lanes,) {
const shouldUpdate =
checkHasForceUpdateAfterProcessing() ||
checkShouldComponentUpdate(
workInProgress,
ctor,
oldProps,
newProps,
oldState,
newState,
nextContext,
);
return shouldUpdate;
}
Copy the code
UpdateClassInstance = updateClassInstance ();
- Is there a
forceUpdate
shouldComponentUpdate
(checkShouldComponentUpdate
)
If Fiber is reusable for ClassComponent, it depends on the props () method if this update is generated by calling forceUpdate.
function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext,) {
const instance = workInProgress.stateNode;
if (typeof instance.shouldComponentUpdate === 'function') {
const shouldUpdate = instance.shouldComponentUpdate(
newProps,
newState,
nextContext,
);
return shouldUpdate;
}
if (ctor.prototype && ctor.prototype.isPureReactComponent) {
return(! shallowEqual(oldProps, newProps) || ! shallowEqual(oldState, newState) ); }return true;
}
Copy the code
According to the return of checkShouldComponentUpdate, when there is no set shouldComponentUpdate method, the effect is equivalent to set the shouldComponentUpdate but constant returns true, The ClassComponent is re-render by various factors.
Let’s see how the Functioncomponent generates Fiber nodes.
FunctionComponent
function updateFunctionComponent(current, workInProgress, Component, nextProps: any, renderLanes,) {
if(current ! = =null && !didReceiveUpdate) {
bailoutHooks(current, workInProgress, renderLanes);
returnbailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); }}Copy the code
Whether the FunctionComponent is Fiber reusable depends on the didReceiveUpdate variable, which is assigned to the beginWork method:
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
){
if( oldProps ! == newProps || hasLegacyContextChanged() || (__DEV__ ? workInProgress.type ! == current.type :false)
) {
didReceiveUpdate = true;
} else if(! includesSomeLane(renderLanes, updateLanes)) {// Enter this judgment when the priority is not enough
didReceiveUpdate = false;
switch(workInProgress.tag) {
// ...
}
// It is possible to execute this if the switch above does not have a return statement
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
} else {
if((current.flags & ForceUpdateForLegacySuspense) ! == NoFlags) { didReceiveUpdate =true;
} else {
didReceiveUpdate = false; }}}Copy the code
The didReceiveUpdate variable is set to false when the FunctionComponent props, context (dev) is unchanged and the update priority is sufficient. The reusable Fiber node is returned after the subsequent execution of the updateFunctionComponent method.
conclusion
Whether workInProgressTree can reuse the Fiber node of currentTree when updating depends on:
ClassComponent
- This update is not a call
forceUpdate
To update the shouldComponentUpdate
To determine that the update does not need to be updated
- This update is not a call
FunctionComponent
dev
Is the element typetype
The sameprops
The samecontext
There is no update- This time the priority is enough
reference
[beginWork/updateFunctionComponent/updateClassComponent/finishClassComponent] github.com/facebook/re…
UpdateClassInstance/checkShouldComponentUpdate github.com/facebook/re…
[checkHasForceUpdateAfterProcessing] github.com/facebook/re…