Expensive
作为 Input
的children传入Input
组件中触发state更新import React, { useState } from 'react'
function Input({ children }) {
const [num, updateNum] = useState(0);
console.log('Input','-----');
return (
<>
{
updateNum(+e.target.value)
}} />
num is {num}
{children}
>
);
}
function Expensive() {
console.log('expensive render');
return
expensive
}
export default function Diff() {
console.log('Diff','-----');
return
}
bailoutOnAlreadyFinishedWork
oldProps === newProps
prepareFreshStack
中根据root.current
创建 workInProgress
时, pendingProps的值为null
// prepareFreshStack
workInProgress = createWorkInProgress(root.current, null);
// beginWork
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps; // null
current.memoizedProps
为null呢? 因为在 performUnitOfWork
中, 当前fiber beginWork结束之后会将 pendingProps(本次更新的属性) 复制给 memoizedProps(上次更新时用的属性)function performUnitOfWork(unitOfWork: Fiber): void {
// The current, flushed, state of this fiber is the alternate. Ideally
// nothing should rely on this, but relying on it here means that we don't
// need an additional field on the work in progress.
const current = unitOfWork.alternate;
setCurrentDebugFiberInDEV(unitOfWork);
let next;
if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
startProfilerTimer(unitOfWork);
next = beginWork(current, unitOfWork, subtreeRenderLanes);
stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
} else {
next = beginWork(current, unitOfWork, subtreeRenderLanes);
}
resetCurrentDebugFiberInDEV();
// 当前fiber beginWork结束之后会将 pendingProps(本次更新的属性) 复制给 memoizedProps(上次更新时用的属性)
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
// If this doesn't spawn new work, complete the current work.
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next;
}
ReactCurrentOwner.current = null;
}
Input
组件 // ...
} else if (!includesSomeLane(renderLanes, updateLanes)) {
didReceiveUpdate = false;
bailoutOnAlreadyFinishedWork
Diff
beginWork中, props没有发生变化
?bailoutOnAlreadyFinishedWork
后,会cloneChildFibers
,从而workInProgress.pendingProps === current.pendingProps成立
// cloneChildFibers
// 注意 这里的pendingProps传递的是 current上的pendingProps
let newChild = createWorkInProgress(currentChild, currentChild.pendingProps);
// createWorkInProgress
const createFiber = function ( tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode, ): Fiber {
return new FiberNode(tag, pendingProps, key, mode);
};
// FiberNode
this.pendingProps = pendingProps;
本次的current
) beginWork结束之后, 会给 memoizedProps赋值为pendingProps
,从而``workInProgress.pendingProps === current.memoizedProps 成立`// performUnitOfWork
unitOfWork.memoizedProps = unitOfWork.pendingProps;
Diff
组件的fiber上没有 Input组件产生
的更新优先级
?childLanes
上(即这里的Diff和FiberRoot都会被合并
, 而Diff组件自己的lanes不会改变
,lanes发生改变的只有Input组件对应fiber
// markUpdateLaneFromFiberToRoot
// 更新产生更新的fiber的lanes
sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
while (parent !== null) {
// 将当前的更新产生的lane 合并的parent的childLanes上
parent.childLanes = mergeLanes(parent.childLanes, lane);
alternate = parent.alternate;
if (alternate !== null) {
alternate.childLanes = mergeLanes(alternate.childLanes, lane);
} else {
if (true) {
if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {
warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
}
}
}
node = parent;
parent = parent.return;
}
bailoutOnAlreadyFinishedWork
,从而跳过当前fiber的beginWork阶段(所以Diff这个函数只会执行一次
). 但是由于子树中的Input
存在更新(即workInProgress.childlans中存在updateLane
),所以直接cloneChildFibers
并返回子fiber(即这里的Input的fiber节点
)Input
的children时(暂时不考虑Fragment
) , Expensive fiber
的 props没有发生变化(因为 Diff组件命中了 bailoutOnAlreadyFinishedWork)
,并且lanes也不在renderLanes
上,所以会触发 bailoutOnAlreadyFinishedWork
if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
// oldProps === newProps true
if (
oldProps !== newProps ||
hasLegacyContextChanged() ||
// Force a re-render if the implementation changed due to hot reload:
(__DEV__ ? workInProgress.type !== current.type : false)
) {
didReceiveUpdate = true;
} else if (!includesSomeLane(renderLanes, updateLanes)) {
didReceiveUpdate = false;
// ...
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
bailoutOnAlreadyFinishedWork
的逻辑中会检查children.childLanes(即children的children是否存在更新)
, 如果不存在更新,则直接跳过子树(这里直接跳过子树
),否则继续调和子树 // Check if the children have any pending work.
// 如果不存在更新,则直接跳过子树
if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {
// The children don't have any work either. We can skip them.
// TODO: Once we add back resuming, we should check if the children are
// a work-in-progress set. If so, we need to transfer their effects.
return null;
} else {
// 如果子树中存在更新,则继续调和子树
// This fiber doesn't have work, but its subtree does. Clone the child
// fibers and continue.
cloneChildFibers(current, workInProgress);