Skip to content

Commit 8f0a977

Browse files
author
Brian Vaughn
committed
Moved resetChildLanes into complete work
This enabled us to remove a few hot path tag-type checks, but did not otherwise change any functionality.
1 parent d38ec17 commit 8f0a977

File tree

2 files changed

+193
-156
lines changed

2 files changed

+193
-156
lines changed

packages/react-reconciler/src/ReactFiberCompleteWork.new.js

Lines changed: 192 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99

1010
import type {Fiber} from './ReactInternalTypes';
11-
import type {Lanes} from './ReactFiberLane';
11+
import type {Lanes, Lane} from './ReactFiberLane';
1212
import type {
1313
ReactFundamentalComponentInstance,
1414
ReactScopeInstance,
@@ -58,14 +58,20 @@ import {
5858
OffscreenComponent,
5959
LegacyHiddenComponent,
6060
} from './ReactWorkTags';
61-
import {NoMode, BlockingMode, ProfileMode} from './ReactTypeOfMode';
61+
import {
62+
NoMode,
63+
BlockingMode,
64+
ConcurrentMode,
65+
ProfileMode,
66+
} from './ReactTypeOfMode';
6267
import {
6368
Ref,
6469
Update,
6570
NoFlags,
6671
DidCapture,
6772
Snapshot,
6873
MutationMask,
74+
StaticMask,
6975
} from './ReactFiberFlags';
7076
import invariant from 'shared/invariant';
7177

@@ -137,9 +143,16 @@ import {
137143
renderHasNotSuspendedYet,
138144
popRenderLanes,
139145
getRenderTargetTime,
146+
subtreeRenderLanes,
140147
} from './ReactFiberWorkLoop.new';
141148
import {createFundamentalStateInstance} from './ReactFiberFundamental.new';
142-
import {OffscreenLane, SomeRetryLane} from './ReactFiberLane';
149+
import {
150+
OffscreenLane,
151+
SomeRetryLane,
152+
NoLanes,
153+
includesSomeLane,
154+
mergeLanes,
155+
} from './ReactFiberLane';
143156
import {resetChildFibers} from './ReactChildFiber.new';
144157
import {createScopeInstance} from './ReactFiberScope.new';
145158
import {transferActualDuration} from './ReactProfilerTimer.new';
@@ -668,6 +681,150 @@ function cutOffTailIfNeeded(
668681
}
669682
}
670683

684+
function bubbleProperties(completedWork: Fiber) {
685+
if (
686+
// TODO: Move this check out of the hot path by moving `resetChildLanes`
687+
// to switch statement in `completeWork`.
688+
(completedWork.tag === LegacyHiddenComponent ||
689+
completedWork.tag === OffscreenComponent) &&
690+
completedWork.memoizedState !== null &&
691+
!includesSomeLane(subtreeRenderLanes, (OffscreenLane: Lane)) &&
692+
(completedWork.mode & ConcurrentMode) !== NoLanes
693+
) {
694+
// The children of this component are hidden. Don't bubble their
695+
// expiration times.
696+
return;
697+
}
698+
699+
const didBailout =
700+
completedWork.alternate !== null &&
701+
completedWork.alternate.child === completedWork.child;
702+
703+
let newChildLanes = NoLanes;
704+
let subtreeFlags = NoFlags;
705+
706+
if (!didBailout) {
707+
// Bubble up the earliest expiration time.
708+
if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
709+
// In profiling mode, resetChildExpirationTime is also used to reset
710+
// profiler durations.
711+
let actualDuration = completedWork.actualDuration;
712+
let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);
713+
714+
let child = completedWork.child;
715+
while (child !== null) {
716+
newChildLanes = mergeLanes(
717+
newChildLanes,
718+
mergeLanes(child.lanes, child.childLanes),
719+
);
720+
721+
subtreeFlags |= child.subtreeFlags;
722+
subtreeFlags |= child.flags;
723+
724+
// When a fiber is cloned, its actualDuration is reset to 0. This value will
725+
// only be updated if work is done on the fiber (i.e. it doesn't bailout).
726+
// When work is done, it should bubble to the parent's actualDuration. If
727+
// the fiber has not been cloned though, (meaning no work was done), then
728+
// this value will reflect the amount of time spent working on a previous
729+
// render. In that case it should not bubble. We determine whether it was
730+
// cloned by comparing the child pointer.
731+
actualDuration += child.actualDuration;
732+
733+
treeBaseDuration += child.treeBaseDuration;
734+
child = child.sibling;
735+
}
736+
737+
const isTimedOutSuspense =
738+
completedWork.tag === SuspenseComponent &&
739+
completedWork.memoizedState !== null;
740+
if (isTimedOutSuspense) {
741+
// Don't count time spent in a timed out Suspense subtree as part of the base duration.
742+
const primaryChildFragment = completedWork.child;
743+
if (primaryChildFragment !== null) {
744+
treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);
745+
}
746+
}
747+
748+
completedWork.actualDuration = actualDuration;
749+
completedWork.treeBaseDuration = treeBaseDuration;
750+
} else {
751+
let child = completedWork.child;
752+
while (child !== null) {
753+
newChildLanes = mergeLanes(
754+
newChildLanes,
755+
mergeLanes(child.lanes, child.childLanes),
756+
);
757+
758+
subtreeFlags |= child.subtreeFlags;
759+
subtreeFlags |= child.flags;
760+
761+
child = child.sibling;
762+
}
763+
}
764+
765+
completedWork.subtreeFlags |= subtreeFlags;
766+
} else {
767+
// Bubble up the earliest expiration time.
768+
if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
769+
// In profiling mode, resetChildExpirationTime is also used to reset
770+
// profiler durations.
771+
let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);
772+
773+
let child = completedWork.child;
774+
while (child !== null) {
775+
newChildLanes = mergeLanes(
776+
newChildLanes,
777+
mergeLanes(child.lanes, child.childLanes),
778+
);
779+
780+
// "Static" flags share the lifetime of the fiber/hook they belong to,
781+
// so we should bubble those up even during a bailout. All the other
782+
// flags have a lifetime only of a single render + commit, so we should
783+
// ignore them.
784+
subtreeFlags |= child.subtreeFlags & StaticMask;
785+
subtreeFlags |= child.flags & StaticMask;
786+
787+
treeBaseDuration += child.treeBaseDuration;
788+
child = child.sibling;
789+
}
790+
791+
const isTimedOutSuspense =
792+
completedWork.tag === SuspenseComponent &&
793+
completedWork.memoizedState !== null;
794+
if (isTimedOutSuspense) {
795+
// Don't count time spent in a timed out Suspense subtree as part of the base duration.
796+
const primaryChildFragment = completedWork.child;
797+
if (primaryChildFragment !== null) {
798+
treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);
799+
}
800+
}
801+
802+
completedWork.treeBaseDuration = treeBaseDuration;
803+
} else {
804+
let child = completedWork.child;
805+
while (child !== null) {
806+
newChildLanes = mergeLanes(
807+
newChildLanes,
808+
mergeLanes(child.lanes, child.childLanes),
809+
);
810+
811+
// "Static" flags share the lifetime of the fiber/hook they belong to,
812+
// so we should bubble those up even during a bailout. All the other
813+
// flags have a lifetime only of a single render + commit, so we should
814+
// ignore them.
815+
subtreeFlags |= child.subtreeFlags & StaticMask;
816+
subtreeFlags |= child.flags & StaticMask;
817+
818+
child = child.sibling;
819+
}
820+
}
821+
822+
completedWork.subtreeFlags |= subtreeFlags;
823+
}
824+
825+
completedWork.childLanes = newChildLanes;
826+
}
827+
671828
function completeWork(
672829
current: Fiber | null,
673830
workInProgress: Fiber,
@@ -686,12 +843,14 @@ function completeWork(
686843
case Profiler:
687844
case ContextConsumer:
688845
case MemoComponent:
846+
bubbleProperties(workInProgress);
689847
return null;
690848
case ClassComponent: {
691849
const Component = workInProgress.type;
692850
if (isLegacyContextProvider(Component)) {
693851
popLegacyContext(workInProgress);
694852
}
853+
bubbleProperties(workInProgress);
695854
return null;
696855
}
697856
case HostRoot: {
@@ -720,6 +879,7 @@ function completeWork(
720879
}
721880
}
722881
updateHostContainer(current, workInProgress);
882+
bubbleProperties(workInProgress);
723883
return null;
724884
}
725885
case HostComponent: {
@@ -746,6 +906,7 @@ function completeWork(
746906
'caused by a bug in React. Please file an issue.',
747907
);
748908
// This can happen when we abort work.
909+
bubbleProperties(workInProgress);
749910
return null;
750911
}
751912

@@ -803,6 +964,7 @@ function completeWork(
803964
markRef(workInProgress);
804965
}
805966
}
967+
bubbleProperties(workInProgress);
806968
return null;
807969
}
808970
case HostText: {
@@ -837,6 +999,7 @@ function completeWork(
837999
);
8381000
}
8391001
}
1002+
bubbleProperties(workInProgress);
8401003
return null;
8411004
}
8421005
case SuspenseComponent: {
@@ -856,6 +1019,7 @@ function completeWork(
8561019
if (enableSchedulerTracing) {
8571020
markSpawnedWork(OffscreenLane);
8581021
}
1022+
bubbleProperties(workInProgress);
8591023
return null;
8601024
} else {
8611025
// We should never have been in a hydration state if we didn't have a current.
@@ -872,6 +1036,7 @@ function completeWork(
8721036
// If something suspended, schedule an effect to attach retry listeners.
8731037
// So we might as well always mark this.
8741038
workInProgress.flags |= Update;
1039+
bubbleProperties(workInProgress);
8751040
return null;
8761041
}
8771042
}
@@ -964,6 +1129,7 @@ function completeWork(
9641129
// Always notify the callback
9651130
workInProgress.flags |= Update;
9661131
}
1132+
bubbleProperties(workInProgress);
9671133
return null;
9681134
}
9691135
case HostPortal:
@@ -972,10 +1138,12 @@ function completeWork(
9721138
if (current === null) {
9731139
preparePortalMount(workInProgress.stateNode.containerInfo);
9741140
}
1141+
bubbleProperties(workInProgress);
9751142
return null;
9761143
case ContextProvider:
9771144
// Pop provider fiber
9781145
popProvider(workInProgress);
1146+
bubbleProperties(workInProgress);
9791147
return null;
9801148
case IncompleteClassComponent: {
9811149
// Same as class component case. I put it down here so that the tags are
@@ -984,6 +1152,7 @@ function completeWork(
9841152
if (isLegacyContextProvider(Component)) {
9851153
popLegacyContext(workInProgress);
9861154
}
1155+
bubbleProperties(workInProgress);
9871156
return null;
9881157
}
9891158
case SuspenseListComponent: {
@@ -995,6 +1164,7 @@ function completeWork(
9951164
if (renderState === null) {
9961165
// We're running in the default, "independent" mode.
9971166
// We don't do anything in this mode.
1167+
bubbleProperties(workInProgress);
9981168
return null;
9991169
}
10001170

@@ -1117,6 +1287,7 @@ function completeWork(
11171287
!getIsHydrating() // We don't cut it if we're hydrating.
11181288
) {
11191289
// We're done.
1290+
bubbleProperties(workInProgress);
11201291
return null;
11211292
}
11221293
} else if (
@@ -1190,6 +1361,7 @@ function completeWork(
11901361
// Do a pass over the next row.
11911362
return next;
11921363
}
1364+
bubbleProperties(workInProgress);
11931365
return null;
11941366
}
11951367
case FundamentalComponent: {
@@ -1217,6 +1389,7 @@ function completeWork(
12171389
): any): Instance);
12181390
fundamentalInstance.instance = instance;
12191391
if (fundamentalImpl.reconcileChildren === false) {
1392+
bubbleProperties(workInProgress);
12201393
return null;
12211394
}
12221395
appendAllChildren(instance, workInProgress, false, false);
@@ -1239,6 +1412,7 @@ function completeWork(
12391412
markUpdate(workInProgress);
12401413
}
12411414
}
1415+
bubbleProperties(workInProgress);
12421416
return null;
12431417
}
12441418
break;
@@ -1261,31 +1435,44 @@ function completeWork(
12611435
markRef(workInProgress);
12621436
}
12631437
}
1438+
bubbleProperties(workInProgress);
12641439
return null;
12651440
}
12661441
break;
12671442
}
12681443
case Block:
12691444
if (enableBlocksAPI) {
1445+
bubbleProperties(workInProgress);
12701446
return null;
12711447
}
12721448
break;
12731449
case OffscreenComponent:
12741450
case LegacyHiddenComponent: {
12751451
popRenderLanes(workInProgress);
1452+
const nextState: OffscreenState | null = workInProgress.memoizedState;
1453+
const nextIsHidden = nextState !== null;
1454+
12761455
if (current !== null) {
1277-
const nextState: OffscreenState | null = workInProgress.memoizedState;
12781456
const prevState: OffscreenState | null = current.memoizedState;
12791457

12801458
const prevIsHidden = prevState !== null;
1281-
const nextIsHidden = nextState !== null;
12821459
if (
12831460
prevIsHidden !== nextIsHidden &&
12841461
newProps.mode !== 'unstable-defer-without-hiding'
12851462
) {
12861463
workInProgress.flags |= Update;
12871464
}
12881465
}
1466+
1467+
// Don't bubble properties for hidden children.
1468+
if (
1469+
!nextIsHidden ||
1470+
includesSomeLane(subtreeRenderLanes, (OffscreenLane: Lane)) ||
1471+
(workInProgress.mode & ConcurrentMode) === NoLanes
1472+
) {
1473+
bubbleProperties(workInProgress);
1474+
}
1475+
12891476
return null;
12901477
}
12911478
}

0 commit comments

Comments
 (0)