Moved resetChildLanes into complete work (#19836)
This allows us to inline a few checks that are specific to a certain tag-type.
Brian Vaughn committed
Sep 15, 2020 at 17:12 UTC
16fb2b6f9e8fb17e2c468384e870cab635e4b1bb
2 files changed
+198
-156
packages/react-reconciler/src/ReactFiberCompleteWork.new.js
+197
-5
@@ -8,7 +8,7 @@
8
*/
9
10
import type {Fiber} from './ReactInternalTypes';
11
-import type {Lanes} from './ReactFiberLane';
11
+import type {Lanes, Lane} from './ReactFiberLane';
12
import type {
13
ReactFundamentalComponentInstance,
14
ReactScopeInstance,
@@ -58,7 +58,12 @@ import {
58
OffscreenComponent,
59
LegacyHiddenComponent,
60
} from './ReactWorkTags';
61
-import {NoMode, BlockingMode, ProfileMode} from './ReactTypeOfMode';
61
+import {
62
+ NoMode,
63
+ BlockingMode,
64
+ ConcurrentMode,
65
+ ProfileMode,
66
+} from './ReactTypeOfMode';
67
import {
68
Ref,
69
Update,
@@ -66,6 +71,7 @@ import {
71
DidCapture,
72
Snapshot,
73
MutationMask,
74
+ StaticMask,
75
} from './ReactFiberFlags';
76
import invariant from 'shared/invariant';
77
@@ -137,9 +143,16 @@ import {
143
renderHasNotSuspendedYet,
144
popRenderLanes,
145
getRenderTargetTime,
146
+ subtreeRenderLanes,
147
} from './ReactFiberWorkLoop.new';
148
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';
156
import {resetChildFibers} from './ReactChildFiber.new';
157
import {createScopeInstance} from './ReactFiberScope.new';
158
import {transferActualDuration} from './ReactProfilerTimer.new';
@@ -668,6 +681,114 @@ function cutOffTailIfNeeded(
681
}
682
}
683
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
+
792
function completeWork(
793
current: Fiber | null,
794
workInProgress: Fiber,
@@ -686,12 +807,14 @@ function completeWork(
807
case Profiler:
808
case ContextConsumer:
809
case MemoComponent:
810
+ bubbleProperties(workInProgress);
811
return null;
812
case ClassComponent: {
813
const Component = workInProgress.type;
814
if (isLegacyContextProvider(Component)) {
815
popLegacyContext(workInProgress);
816
}
817
+ bubbleProperties(workInProgress);
818
return null;
819
}
820
case HostRoot: {
@@ -720,6 +843,7 @@ function completeWork(
843
}
844
}
845
updateHostContainer(current, workInProgress);
846
+ bubbleProperties(workInProgress);
847
return null;
848
}
849
case HostComponent: {
@@ -746,6 +870,7 @@ function completeWork(
870
'caused by a bug in React. Please file an issue.',
871
);
872
// This can happen when we abort work.
873
+ bubbleProperties(workInProgress);
874
return null;
875
}
876
@@ -803,6 +928,7 @@ function completeWork(
928
markRef(workInProgress);
929
}
930
}
931
+ bubbleProperties(workInProgress);
932
return null;
933
}
934
case HostText: {
@@ -837,6 +963,7 @@ function completeWork(
963
);
964
}
965
}
966
+ bubbleProperties(workInProgress);
967
return null;
968
}
969
case SuspenseComponent: {
@@ -856,6 +983,20 @@ function completeWork(
983
if (enableSchedulerTracing) {
984
markSpawnedWork(OffscreenLane);
985
}
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
+ }
1000
return null;
1001
} else {
1002
// We should never have been in a hydration state if we didn't have a current.
@@ -872,6 +1013,20 @@ function completeWork(
1013
// If something suspended, schedule an effect to attach retry listeners.
1014
// So we might as well always mark this.
1015
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
+ }
1030
return null;
1031
}
1032
}
@@ -887,6 +1042,7 @@ function completeWork(
1042
) {
1043
transferActualDuration(workInProgress);
1044
}
1045
+ // Don't bubble properties in this case.
1046
return workInProgress;
1047
}
1048
@@ -964,6 +1120,19 @@ function completeWork(
1120
// Always notify the callback
1121
workInProgress.flags |= Update;
1122
}
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
+ }
1136
return null;
1137
}
1138
case HostPortal:
@@ -972,10 +1141,12 @@ function completeWork(
1141
if (current === null) {
1142
preparePortalMount(workInProgress.stateNode.containerInfo);
1143
}
1144
+ bubbleProperties(workInProgress);
1145
return null;
1146
case ContextProvider:
1147
// Pop provider fiber
1148
popProvider(workInProgress);
1149
+ bubbleProperties(workInProgress);
1150
return null;
1151
case IncompleteClassComponent: {
1152
// Same as class component case. I put it down here so that the tags are
@@ -984,6 +1155,7 @@ function completeWork(
1155
if (isLegacyContextProvider(Component)) {
1156
popLegacyContext(workInProgress);
1157
}
1158
+ bubbleProperties(workInProgress);
1159
return null;
1160
}
1161
case SuspenseListComponent: {
@@ -995,6 +1167,7 @@ function completeWork(
1167
if (renderState === null) {
1168
// We're running in the default, "independent" mode.
1169
// We don't do anything in this mode.
1170
+ bubbleProperties(workInProgress);
1171
return null;
1172
}
1173
@@ -1060,6 +1233,7 @@ function completeWork(
1233
ForceSuspenseFallback,
1234
),
1235
);
1236
+ // Don't bubble properties in this case.
1237
return workInProgress.child;
1238
}
1239
row = row.sibling;
@@ -1117,6 +1291,7 @@ function completeWork(
1291
!getIsHydrating() // We don't cut it if we're hydrating.
1292
) {
1293
// We're done.
1294
+ bubbleProperties(workInProgress);
1295
return null;
1296
}
1297
} else if (
@@ -1188,8 +1363,10 @@ function completeWork(
1363
}
1364
pushSuspenseContext(workInProgress, suspenseContext);
1365
// Do a pass over the next row.
1366
+ // Don't bubble properties in this case.
1367
return next;
1368
}
1369
+ bubbleProperties(workInProgress);
1370
return null;
1371
}
1372
case FundamentalComponent: {
@@ -1217,6 +1394,7 @@ function completeWork(
1394
): any): Instance);
1395
fundamentalInstance.instance = instance;
1396
if (fundamentalImpl.reconcileChildren === false) {
1397
+ bubbleProperties(workInProgress);
1398
return null;
1399
}
1400
appendAllChildren(instance, workInProgress, false, false);
@@ -1239,6 +1417,7 @@ function completeWork(
1417
markUpdate(workInProgress);
1418
}
1419
}
1420
+ bubbleProperties(workInProgress);
1421
return null;
1422
}
1423
break;
@@ -1261,24 +1440,27 @@ function completeWork(
1440
markRef(workInProgress);
1441
}
1442
}
1443
+ bubbleProperties(workInProgress);
1444
return null;
1445
}
1446
break;
1447
}
1448
case Block:
1449
if (enableBlocksAPI) {
1450
+ bubbleProperties(workInProgress);
1451
return null;
1452
}
1453
break;
1454
case OffscreenComponent:
1455
case LegacyHiddenComponent: {
1456
popRenderLanes(workInProgress);
1457
+ const nextState: OffscreenState | null = workInProgress.memoizedState;
1458
+ const nextIsHidden = nextState !== null;
1459
+
1460
if (current !== null) {
1277
- const nextState: OffscreenState | null = workInProgress.memoizedState;
1461
const prevState: OffscreenState | null = current.memoizedState;
1462
1463
const prevIsHidden = prevState !== null;
1281
- const nextIsHidden = nextState !== null;
1464
if (
1465
prevIsHidden !== nextIsHidden &&
1466
newProps.mode !== 'unstable-defer-without-hiding'
@@ -1286,6 +1468,16 @@ function completeWork(
1468
workInProgress.flags |= Update;
1469
}
1470
}
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
+
1481
return null;
1482
}
1483
}
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+1
-151
@@ -115,8 +115,6 @@ import {
115
MemoComponent,
116
SimpleMemoComponent,
117
Block,
118
- OffscreenComponent,
119
- LegacyHiddenComponent,
118
ScopeComponent,
119
} from './ReactWorkTags';
120
import {LegacyRoot} from './ReactRootTags';
@@ -139,7 +137,6 @@ import {
137
MutationMask,
138
LayoutMask,
139
PassiveMask,
142
- StaticMask,
140
} from './ReactFiberFlags';
141
import {
142
NoLanePriority,
@@ -151,7 +148,6 @@ import {
148
NoLane,
149
SyncLane,
150
SyncBatchedLane,
154
- OffscreenLane,
151
NoTimestamp,
152
findUpdateLane,
153
findTransitionLane,
@@ -290,7 +286,7 @@ let workInProgressRootRenderLanes: Lanes = NoLanes;
286
//
287
// Most things in the work loop should deal with workInProgressRootRenderLanes.
288
// Most things in begin/complete phases should deal with subtreeRenderLanes.
293
-let subtreeRenderLanes: Lanes = NoLanes;
289
+export let subtreeRenderLanes: Lanes = NoLanes;
290
const subtreeRenderLanesCursor: StackCursor<Lanes> = createCursor(NoLanes);
291
292
// Whether to root completed, errored, suspended, etc.
@@ -1719,8 +1715,6 @@ function completeUnitOfWork(unitOfWork: Fiber): void {
1715
workInProgress = next;
1716
return;
1717
}
1722
-
1723
- resetChildLanes(completedWork);
1718
} else {
1719
// This fiber did not complete because something threw. Pop values off
1720
// the stack without entering the complete phase. If this is a boundary,
@@ -1782,150 +1776,6 @@ function completeUnitOfWork(unitOfWork: Fiber): void {
1776
}
1777
}
1778
1785
-function resetChildLanes(completedWork: Fiber) {
1786
- if (
1787
- // TODO: Move this check out of the hot path by moving `resetChildLanes`
1788
- // to switch statement in `completeWork`.
1789
- (completedWork.tag === LegacyHiddenComponent ||
1790
- completedWork.tag === OffscreenComponent) &&
1791
- completedWork.memoizedState !== null &&
1792
- !includesSomeLane(subtreeRenderLanes, (OffscreenLane: Lane)) &&
1793
- (completedWork.mode & ConcurrentMode) !== NoLanes
1794
- ) {
1795
- // The children of this component are hidden. Don't bubble their
1796
- // expiration times.
1797
- return;
1798
- }
1799
-
1800
- const didBailout =
1801
- completedWork.alternate !== null &&
1802
- completedWork.alternate.child === completedWork.child;
1803
-
1804
- let newChildLanes = NoLanes;
1805
- let subtreeFlags = NoFlags;
1806
-
1807
- if (!didBailout) {
1808
- // Bubble up the earliest expiration time.
1809
- if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
1810
- // In profiling mode, resetChildExpirationTime is also used to reset
1811
- // profiler durations.
1812
- let actualDuration = completedWork.actualDuration;
1813
- let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);
1814
-
1815
- let child = completedWork.child;
1816
- while (child !== null) {
1817
- newChildLanes = mergeLanes(
1818
- newChildLanes,
1819
- mergeLanes(child.lanes, child.childLanes),
1820
- );
1821
-
1822
- subtreeFlags |= child.subtreeFlags;
1823
- subtreeFlags |= child.flags;
1824
-
1825
- // When a fiber is cloned, its actualDuration is reset to 0. This value will
1826
- // only be updated if work is done on the fiber (i.e. it doesn't bailout).
1827
- // When work is done, it should bubble to the parent's actualDuration. If
1828
- // the fiber has not been cloned though, (meaning no work was done), then
1829
- // this value will reflect the amount of time spent working on a previous
1830
- // render. In that case it should not bubble. We determine whether it was
1831
- // cloned by comparing the child pointer.
1832
- actualDuration += child.actualDuration;
1833
-
1834
- treeBaseDuration += child.treeBaseDuration;
1835
- child = child.sibling;
1836
- }
1837
-
1838
- const isTimedOutSuspense =
1839
- completedWork.tag === SuspenseComponent &&
1840
- completedWork.memoizedState !== null;
1841
- if (isTimedOutSuspense) {
1842
- // Don't count time spent in a timed out Suspense subtree as part of the base duration.
1843
- const primaryChildFragment = completedWork.child;
1844
- if (primaryChildFragment !== null) {
1845
- treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);
1846
- }
1847
- }
1848
-
1849
- completedWork.actualDuration = actualDuration;
1850
- completedWork.treeBaseDuration = treeBaseDuration;
1851
- } else {
1852
- let child = completedWork.child;
1853
- while (child !== null) {
1854
- newChildLanes = mergeLanes(
1855
- newChildLanes,
1856
- mergeLanes(child.lanes, child.childLanes),
1857
- );
1858
-
1859
- subtreeFlags |= child.subtreeFlags;
1860
- subtreeFlags |= child.flags;
1861
-
1862
- child = child.sibling;
1863
- }
1864
- }
1865
-
1866
- completedWork.subtreeFlags |= subtreeFlags;
1867
- } else {
1868
- // Bubble up the earliest expiration time.
1869
- if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
1870
- // In profiling mode, resetChildExpirationTime is also used to reset
1871
- // profiler durations.
1872
- let treeBaseDuration = ((completedWork.selfBaseDuration: any): number);
1873
-
1874
- let child = completedWork.child;
1875
- while (child !== null) {
1876
- newChildLanes = mergeLanes(
1877
- newChildLanes,
1878
- mergeLanes(child.lanes, child.childLanes),
1879
- );
1880
-
1881
- // "Static" flags share the lifetime of the fiber/hook they belong to,
1882
- // so we should bubble those up even during a bailout. All the other
1883
- // flags have a lifetime only of a single render + commit, so we should
1884
- // ignore them.
1885
- subtreeFlags |= child.subtreeFlags & StaticMask;
1886
- subtreeFlags |= child.flags & StaticMask;
1887
-
1888
- treeBaseDuration += child.treeBaseDuration;
1889
- child = child.sibling;
1890
- }
1891
-
1892
- const isTimedOutSuspense =
1893
- completedWork.tag === SuspenseComponent &&
1894
- completedWork.memoizedState !== null;
1895
- if (isTimedOutSuspense) {
1896
- // Don't count time spent in a timed out Suspense subtree as part of the base duration.
1897
- const primaryChildFragment = completedWork.child;
1898
- if (primaryChildFragment !== null) {
1899
- treeBaseDuration -= ((primaryChildFragment.treeBaseDuration: any): number);
1900
- }
1901
- }
1902
-
1903
- completedWork.treeBaseDuration = treeBaseDuration;
1904
- } else {
1905
- let child = completedWork.child;
1906
- while (child !== null) {
1907
- newChildLanes = mergeLanes(
1908
- newChildLanes,
1909
- mergeLanes(child.lanes, child.childLanes),
1910
- );
1911
-
1912
- // "Static" flags share the lifetime of the fiber/hook they belong to,
1913
- // so we should bubble those up even during a bailout. All the other
1914
- // flags have a lifetime only of a single render + commit, so we should
1915
- // ignore them.
1916
- subtreeFlags |= child.subtreeFlags & StaticMask;
1917
- subtreeFlags |= child.flags & StaticMask;
1918
-
1919
- child = child.sibling;
1920
- }
1921
- }
1922
-
1923
- completedWork.subtreeFlags |= subtreeFlags;
1924
- }
1925
-
1926
- completedWork.childLanes = newChildLanes;
1927
-}
1928
-
1779
function commitRoot(root) {
1780
const renderPriorityLevel = getCurrentPriorityLevel();
1781
runWithPriority(