Skip to content

Commit 16fb2b6

Browse files
author
Brian Vaughn
authored
Moved resetChildLanes into complete work (#19836)
This allows us to inline a few checks that are specific to a certain tag-type.
1 parent b93f3e7 commit 16fb2b6

File tree

2 files changed

+198
-156
lines changed

2 files changed

+198
-156
lines changed

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

+197-5
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,114 @@ function cutOffTailIfNeeded(
668681
}
669682
}
670683

684+
function bubbleProperties(completedWork: Fiber) {
685+
const didBailout =
686+
completedWork.alternate !== null &&
687+
completedWork.alternate.child === completedWork.child;
688+
689+
let newChildLanes = NoLanes;
690+
let subtreeFlags = NoFlags;
691+
692+
if (!didBailout) {
693+
// Bubble up the earliest expiration time.
694+
if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
695+
// In profiling mode, resetChildExpirationTime is also used to reset
696+
// profiler durations.
697+
let actualDuration = completedWork.actualDuration;
698+
let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);
699+
700+
let child = completedWork.child;
701+
while (child !== null) {
702+
newChildLanes = mergeLanes(
703+
newChildLanes,
704+
mergeLanes(child.lanes, child.childLanes),
705+
);
706+
707+
subtreeFlags |= child.subtreeFlags;
708+
subtreeFlags |= child.flags;
709+
710+
// When a fiber is cloned, its actualDuration is reset to 0. This value will
711+
// only be updated if work is done on the fiber (i.e. it doesn't bailout).
712+
// When work is done, it should bubble to the parent's actualDuration. If
713+
// the fiber has not been cloned though, (meaning no work was done), then
714+
// this value will reflect the amount of time spent working on a previous
715+
// render. In that case it should not bubble. We determine whether it was
716+
// cloned by comparing the child pointer.
717+
actualDuration += child.actualDuration;
718+
719+
treeBaseDuration += child.treeBaseDuration;
720+
child = child.sibling;
721+
}
722+
723+
completedWork.actualDuration = actualDuration;
724+
completedWork.treeBaseDuration = treeBaseDuration;
725+
} else {
726+
let child = completedWork.child;
727+
while (child !== null) {
728+
newChildLanes = mergeLanes(
729+
newChildLanes,
730+
mergeLanes(child.lanes, child.childLanes),
731+
);
732+
733+
subtreeFlags |= child.subtreeFlags;
734+
subtreeFlags |= child.flags;
735+
736+
child = child.sibling;
737+
}
738+
}
739+
740+
completedWork.subtreeFlags |= subtreeFlags;
741+
} else {
742+
// Bubble up the earliest expiration time.
743+
if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
744+
// In profiling mode, resetChildExpirationTime is also used to reset
745+
// profiler durations.
746+
let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);
747+
748+
let child = completedWork.child;
749+
while (child !== null) {
750+
newChildLanes = mergeLanes(
751+
newChildLanes,
752+
mergeLanes(child.lanes, child.childLanes),
753+
);
754+
755+
// "Static" flags share the lifetime of the fiber/hook they belong to,
756+
// so we should bubble those up even during a bailout. All the other
757+
// flags have a lifetime only of a single render + commit, so we should
758+
// ignore them.
759+
subtreeFlags |= child.subtreeFlags & StaticMask;
760+
subtreeFlags |= child.flags & StaticMask;
761+
762+
treeBaseDuration += child.treeBaseDuration;
763+
child = child.sibling;
764+
}
765+
766+
completedWork.treeBaseDuration = treeBaseDuration;
767+
} else {
768+
let child = completedWork.child;
769+
while (child !== null) {
770+
newChildLanes = mergeLanes(
771+
newChildLanes,
772+
mergeLanes(child.lanes, child.childLanes),
773+
);
774+
775+
// "Static" flags share the lifetime of the fiber/hook they belong to,
776+
// so we should bubble those up even during a bailout. All the other
777+
// flags have a lifetime only of a single render + commit, so we should
778+
// ignore them.
779+
subtreeFlags |= child.subtreeFlags & StaticMask;
780+
subtreeFlags |= child.flags & StaticMask;
781+
782+
child = child.sibling;
783+
}
784+
}
785+
786+
completedWork.subtreeFlags |= subtreeFlags;
787+
}
788+
789+
completedWork.childLanes = newChildLanes;
790+
}
791+
671792
function completeWork(
672793
current: Fiber | null,
673794
workInProgress: Fiber,
@@ -686,12 +807,14 @@ function completeWork(
686807
case Profiler:
687808
case ContextConsumer:
688809
case MemoComponent:
810+
bubbleProperties(workInProgress);
689811
return null;
690812
case ClassComponent: {
691813
const Component = workInProgress.type;
692814
if (isLegacyContextProvider(Component)) {
693815
popLegacyContext(workInProgress);
694816
}
817+
bubbleProperties(workInProgress);
695818
return null;
696819
}
697820
case HostRoot: {
@@ -720,6 +843,7 @@ function completeWork(
720843
}
721844
}
722845
updateHostContainer(current, workInProgress);
846+
bubbleProperties(workInProgress);
723847
return null;
724848
}
725849
case HostComponent: {
@@ -746,6 +870,7 @@ function completeWork(
746870
'caused by a bug in React. Please file an issue.',
747871
);
748872
// This can happen when we abort work.
873+
bubbleProperties(workInProgress);
749874
return null;
750875
}
751876

@@ -803,6 +928,7 @@ function completeWork(
803928
markRef(workInProgress);
804929
}
805930
}
931+
bubbleProperties(workInProgress);
806932
return null;
807933
}
808934
case HostText: {
@@ -837,6 +963,7 @@ function completeWork(
837963
);
838964
}
839965
}
966+
bubbleProperties(workInProgress);
840967
return null;
841968
}
842969
case SuspenseComponent: {
@@ -856,6 +983,20 @@ function completeWork(
856983
if (enableSchedulerTracing) {
857984
markSpawnedWork(OffscreenLane);
858985
}
986+
bubbleProperties(workInProgress);
987+
if (enableProfilerTimer) {
988+
if ((workInProgress.mode & ProfileMode) !== NoMode) {
989+
const isTimedOutSuspense = nextState !== null;
990+
if (isTimedOutSuspense) {
991+
// Don't count time spent in a timed out Suspense subtree as part of the base duration.
992+
const primaryChildFragment = workInProgress.child;
993+
if (primaryChildFragment !== null) {
994+
// $FlowFixMe Flow doens't support type casting in combiation with the -= operator
995+
workInProgress.treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);
996+
}
997+
}
998+
}
999+
}
8591000
return null;
8601001
} else {
8611002
// We should never have been in a hydration state if we didn't have a current.
@@ -872,6 +1013,20 @@ function completeWork(
8721013
// If something suspended, schedule an effect to attach retry listeners.
8731014
// So we might as well always mark this.
8741015
workInProgress.flags |= Update;
1016+
bubbleProperties(workInProgress);
1017+
if (enableProfilerTimer) {
1018+
if ((workInProgress.mode & ProfileMode) !== NoMode) {
1019+
const isTimedOutSuspense = nextState !== null;
1020+
if (isTimedOutSuspense) {
1021+
// Don't count time spent in a timed out Suspense subtree as part of the base duration.
1022+
const primaryChildFragment = workInProgress.child;
1023+
if (primaryChildFragment !== null) {
1024+
// $FlowFixMe Flow doens't support type casting in combiation with the -= operator
1025+
workInProgress.treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);
1026+
}
1027+
}
1028+
}
1029+
}
8751030
return null;
8761031
}
8771032
}
@@ -887,6 +1042,7 @@ function completeWork(
8871042
) {
8881043
transferActualDuration(workInProgress);
8891044
}
1045+
// Don't bubble properties in this case.
8901046
return workInProgress;
8911047
}
8921048

@@ -964,6 +1120,19 @@ function completeWork(
9641120
// Always notify the callback
9651121
workInProgress.flags |= Update;
9661122
}
1123+
bubbleProperties(workInProgress);
1124+
if (enableProfilerTimer) {
1125+
if ((workInProgress.mode & ProfileMode) !== NoMode) {
1126+
if (nextDidTimeout) {
1127+
// Don't count time spent in a timed out Suspense subtree as part of the base duration.
1128+
const primaryChildFragment = workInProgress.child;
1129+
if (primaryChildFragment !== null) {
1130+
// $FlowFixMe Flow doens't support type casting in combiation with the -= operator
1131+
workInProgress.treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);
1132+
}
1133+
}
1134+
}
1135+
}
9671136
return null;
9681137
}
9691138
case HostPortal:
@@ -972,10 +1141,12 @@ function completeWork(
9721141
if (current === null) {
9731142
preparePortalMount(workInProgress.stateNode.containerInfo);
9741143
}
1144+
bubbleProperties(workInProgress);
9751145
return null;
9761146
case ContextProvider:
9771147
// Pop provider fiber
9781148
popProvider(workInProgress);
1149+
bubbleProperties(workInProgress);
9791150
return null;
9801151
case IncompleteClassComponent: {
9811152
// Same as class component case. I put it down here so that the tags are
@@ -984,6 +1155,7 @@ function completeWork(
9841155
if (isLegacyContextProvider(Component)) {
9851156
popLegacyContext(workInProgress);
9861157
}
1158+
bubbleProperties(workInProgress);
9871159
return null;
9881160
}
9891161
case SuspenseListComponent: {
@@ -995,6 +1167,7 @@ function completeWork(
9951167
if (renderState === null) {
9961168
// We're running in the default, "independent" mode.
9971169
// We don't do anything in this mode.
1170+
bubbleProperties(workInProgress);
9981171
return null;
9991172
}
10001173

@@ -1060,6 +1233,7 @@ function completeWork(
10601233
ForceSuspenseFallback,
10611234
),
10621235
);
1236+
// Don't bubble properties in this case.
10631237
return workInProgress.child;
10641238
}
10651239
row = row.sibling;
@@ -1117,6 +1291,7 @@ function completeWork(
11171291
!getIsHydrating() // We don't cut it if we're hydrating.
11181292
) {
11191293
// We're done.
1294+
bubbleProperties(workInProgress);
11201295
return null;
11211296
}
11221297
} else if (
@@ -1188,8 +1363,10 @@ function completeWork(
11881363
}
11891364
pushSuspenseContext(workInProgress, suspenseContext);
11901365
// Do a pass over the next row.
1366+
// Don't bubble properties in this case.
11911367
return next;
11921368
}
1369+
bubbleProperties(workInProgress);
11931370
return null;
11941371
}
11951372
case FundamentalComponent: {
@@ -1217,6 +1394,7 @@ function completeWork(
12171394
): any): Instance);
12181395
fundamentalInstance.instance = instance;
12191396
if (fundamentalImpl.reconcileChildren === false) {
1397+
bubbleProperties(workInProgress);
12201398
return null;
12211399
}
12221400
appendAllChildren(instance, workInProgress, false, false);
@@ -1239,6 +1417,7 @@ function completeWork(
12391417
markUpdate(workInProgress);
12401418
}
12411419
}
1420+
bubbleProperties(workInProgress);
12421421
return null;
12431422
}
12441423
break;
@@ -1261,31 +1440,44 @@ function completeWork(
12611440
markRef(workInProgress);
12621441
}
12631442
}
1443+
bubbleProperties(workInProgress);
12641444
return null;
12651445
}
12661446
break;
12671447
}
12681448
case Block:
12691449
if (enableBlocksAPI) {
1450+
bubbleProperties(workInProgress);
12701451
return null;
12711452
}
12721453
break;
12731454
case OffscreenComponent:
12741455
case LegacyHiddenComponent: {
12751456
popRenderLanes(workInProgress);
1457+
const nextState: OffscreenState | null = workInProgress.memoizedState;
1458+
const nextIsHidden = nextState !== null;
1459+
12761460
if (current !== null) {
1277-
const nextState: OffscreenState | null = workInProgress.memoizedState;
12781461
const prevState: OffscreenState | null = current.memoizedState;
12791462

12801463
const prevIsHidden = prevState !== null;
1281-
const nextIsHidden = nextState !== null;
12821464
if (
12831465
prevIsHidden !== nextIsHidden &&
12841466
newProps.mode !== 'unstable-defer-without-hiding'
12851467
) {
12861468
workInProgress.flags |= Update;
12871469
}
12881470
}
1471+
1472+
// Don't bubble properties for hidden children.
1473+
if (
1474+
!nextIsHidden ||
1475+
includesSomeLane(subtreeRenderLanes, (OffscreenLane: Lane)) ||
1476+
(workInProgress.mode & ConcurrentMode) === NoMode
1477+
) {
1478+
bubbleProperties(workInProgress);
1479+
}
1480+
12891481
return null;
12901482
}
12911483
}

0 commit comments

Comments
 (0)