8
8
*/
9
9
10
10
import type { Fiber } from './ReactInternalTypes' ;
11
- import type { Lanes } from './ReactFiberLane' ;
11
+ import type { Lanes , Lane } from './ReactFiberLane' ;
12
12
import type {
13
13
ReactFundamentalComponentInstance ,
14
14
ReactScopeInstance ,
@@ -58,16 +58,30 @@ import {
58
58
OffscreenComponent ,
59
59
LegacyHiddenComponent ,
60
60
} from './ReactWorkTags' ;
61
- import { NoMode , BlockingMode , ProfileMode } from './ReactTypeOfMode' ;
61
+ import {
62
+ NoMode ,
63
+ BlockingMode ,
64
+ ProfileMode ,
65
+ ConcurrentMode ,
66
+ } from './ReactTypeOfMode' ;
62
67
import {
63
68
Ref ,
64
69
Update ,
65
70
NoEffect ,
66
71
DidCapture ,
67
72
Snapshot ,
73
+ BeforeMutationMask ,
68
74
MutationMask ,
75
+ LayoutMask ,
76
+ PassiveMask ,
69
77
} from './ReactSideEffectTags' ;
70
- import { NoEffect as NoSubtreeTag , Mutation } from './ReactSubtreeTags' ;
78
+ import {
79
+ NoEffect as NoSubtreeTag ,
80
+ BeforeMutation as BeforeMutationSubtreeTag ,
81
+ Mutation as MutationSubtreeTag ,
82
+ Layout as LayoutSubtreeTag ,
83
+ Passive as PassiveSubtreeTag ,
84
+ } from './ReactSubtreeTags' ;
71
85
import invariant from 'shared/invariant' ;
72
86
73
87
import {
@@ -138,9 +152,15 @@ import {
138
152
renderDidSuspendDelayIfPossible ,
139
153
renderHasNotSuspendedYet ,
140
154
popRenderLanes ,
155
+ subtreeRenderLanes ,
141
156
} from './ReactFiberWorkLoop.new' ;
142
157
import { createFundamentalStateInstance } from './ReactFiberFundamental.new' ;
143
- import { OffscreenLane } from './ReactFiberLane' ;
158
+ import {
159
+ includesSomeLane ,
160
+ OffscreenLane ,
161
+ mergeLanes ,
162
+ NoLanes ,
163
+ } from './ReactFiberLane' ;
144
164
import { resetChildFibers } from './ReactChildFiber.new' ;
145
165
import { updateDeprecatedEventListeners } from './ReactFiberDeprecatedEvents.new' ;
146
166
import { createScopeInstance } from './ReactFiberScope.new' ;
@@ -167,7 +187,7 @@ function hadNoMutationsEffects(current: null | Fiber, completedWork: Fiber) {
167
187
if ( ( child . effectTag & MutationMask ) !== NoEffect ) {
168
188
return false ;
169
189
}
170
- if ( ( child . subtreeTag & Mutation ) !== NoSubtreeTag ) {
190
+ if ( ( child . subtreeTag & MutationSubtreeTag ) !== NoSubtreeTag ) {
171
191
return false ;
172
192
}
173
193
child = child . sibling ;
@@ -670,6 +690,124 @@ function cutOffTailIfNeeded(
670
690
}
671
691
}
672
692
693
+ function bubbleProperties ( completedWork : Fiber ) : void {
694
+ const didBailout =
695
+ completedWork . alternate !== null &&
696
+ completedWork . alternate . child === completedWork . child ;
697
+
698
+ let newChildLanes = NoLanes ;
699
+ let subtreeTag = NoSubtreeTag ;
700
+
701
+ if ( ! didBailout ) {
702
+ // Bubble up the earliest expiration time.
703
+ if ( enableProfilerTimer && ( completedWork . mode & ProfileMode ) !== NoMode ) {
704
+ // In profiling mode, resetChildExpirationTime is also used to reset
705
+ // profiler durations.
706
+ let actualDuration = completedWork . actualDuration ;
707
+ let treeBaseDuration = ( ( completedWork . selfBaseDuration : any ) : number ) ;
708
+
709
+ let child = completedWork . child ;
710
+ while ( child !== null ) {
711
+ newChildLanes = mergeLanes (
712
+ newChildLanes ,
713
+ mergeLanes ( child . lanes , child . childLanes ) ,
714
+ ) ;
715
+
716
+ subtreeTag |= child . subtreeTag ;
717
+
718
+ const effectTag = child . effectTag ;
719
+ if ( ( effectTag & BeforeMutationMask ) !== NoEffect ) {
720
+ subtreeTag |= BeforeMutationSubtreeTag ;
721
+ }
722
+ if ( ( effectTag & MutationMask ) !== NoEffect ) {
723
+ subtreeTag |= MutationSubtreeTag ;
724
+ }
725
+ if ( ( effectTag & LayoutMask ) !== NoEffect ) {
726
+ subtreeTag |= LayoutSubtreeTag ;
727
+ }
728
+ if ( ( effectTag & PassiveMask ) !== NoEffect ) {
729
+ subtreeTag |= PassiveSubtreeTag ;
730
+ }
731
+
732
+ // When a fiber is cloned, its actualDuration is reset to 0. This value will
733
+ // only be updated if work is done on the fiber (i.e. it doesn't bailout).
734
+ // When work is done, it should bubble to the parent's actualDuration. If
735
+ // the fiber has not been cloned though, (meaning no work was done), then
736
+ // this value will reflect the amount of time spent working on a previous
737
+ // render. In that case it should not bubble. We determine whether it was
738
+ // cloned by comparing the child pointer.
739
+ actualDuration += child . actualDuration ;
740
+
741
+ treeBaseDuration += child . treeBaseDuration ;
742
+ child = child . sibling ;
743
+ }
744
+
745
+ completedWork . actualDuration = actualDuration ;
746
+ completedWork . treeBaseDuration = treeBaseDuration ;
747
+ } else {
748
+ let child = completedWork . child ;
749
+ while ( child !== null ) {
750
+ newChildLanes = mergeLanes (
751
+ newChildLanes ,
752
+ mergeLanes ( child . lanes , child . childLanes ) ,
753
+ ) ;
754
+
755
+ subtreeTag |= child . subtreeTag ;
756
+
757
+ const effectTag = child . effectTag ;
758
+ if ( ( effectTag & BeforeMutationMask ) !== NoEffect ) {
759
+ subtreeTag |= BeforeMutationSubtreeTag ;
760
+ }
761
+ if ( ( effectTag & MutationMask ) !== NoEffect ) {
762
+ subtreeTag |= MutationSubtreeTag ;
763
+ }
764
+ if ( ( effectTag & LayoutMask ) !== NoEffect ) {
765
+ subtreeTag |= LayoutSubtreeTag ;
766
+ }
767
+ if ( ( effectTag & PassiveMask ) !== NoEffect ) {
768
+ subtreeTag |= PassiveSubtreeTag ;
769
+ }
770
+
771
+ child = child . sibling ;
772
+ }
773
+ }
774
+
775
+ completedWork . subtreeTag |= subtreeTag ;
776
+ } else {
777
+ // Bubble up the earliest expiration time.
778
+ if ( enableProfilerTimer && ( completedWork . mode & ProfileMode ) !== NoMode ) {
779
+ // In profiling mode, resetChildExpirationTime is also used to reset
780
+ // profiler durations.
781
+ let treeBaseDuration = ( ( completedWork . selfBaseDuration : any ) : number ) ;
782
+
783
+ let child = completedWork . child ;
784
+ while ( child !== null ) {
785
+ newChildLanes = mergeLanes (
786
+ newChildLanes ,
787
+ mergeLanes ( child . lanes , child . childLanes ) ,
788
+ ) ;
789
+
790
+ treeBaseDuration += child . treeBaseDuration ;
791
+ child = child . sibling ;
792
+ }
793
+
794
+ completedWork . treeBaseDuration = treeBaseDuration ;
795
+ } else {
796
+ let child = completedWork . child ;
797
+ while ( child !== null ) {
798
+ newChildLanes = mergeLanes (
799
+ newChildLanes ,
800
+ mergeLanes ( child . lanes , child . childLanes ) ,
801
+ ) ;
802
+
803
+ child = child . sibling ;
804
+ }
805
+ }
806
+ }
807
+
808
+ completedWork . childLanes = newChildLanes ;
809
+ }
810
+
673
811
function completeWork (
674
812
current : Fiber | null ,
675
813
workInProgress : Fiber ,
@@ -688,12 +826,14 @@ function completeWork(
688
826
case Profiler :
689
827
case ContextConsumer :
690
828
case MemoComponent :
829
+ bubbleProperties ( workInProgress ) ;
691
830
return null ;
692
831
case ClassComponent : {
693
832
const Component = workInProgress . type ;
694
833
if ( isLegacyContextProvider ( Component ) ) {
695
834
popLegacyContext ( workInProgress ) ;
696
835
}
836
+ bubbleProperties ( workInProgress ) ;
697
837
return null ;
698
838
}
699
839
case HostRoot : {
@@ -722,6 +862,7 @@ function completeWork(
722
862
}
723
863
}
724
864
updateHostContainer ( current , workInProgress ) ;
865
+ bubbleProperties ( workInProgress ) ;
725
866
return null ;
726
867
}
727
868
case HostComponent : {
@@ -756,6 +897,7 @@ function completeWork(
756
897
'caused by a bug in React. Please file an issue.' ,
757
898
) ;
758
899
// This can happen when we abort work.
900
+ bubbleProperties ( workInProgress ) ;
759
901
return null ;
760
902
}
761
903
@@ -835,6 +977,8 @@ function completeWork(
835
977
markRef ( workInProgress ) ;
836
978
}
837
979
}
980
+
981
+ bubbleProperties ( workInProgress ) ;
838
982
return null ;
839
983
}
840
984
case HostText : {
@@ -869,6 +1013,7 @@ function completeWork(
869
1013
) ;
870
1014
}
871
1015
}
1016
+ bubbleProperties ( workInProgress ) ;
872
1017
return null ;
873
1018
}
874
1019
case SuspenseComponent : {
@@ -888,6 +1033,20 @@ function completeWork(
888
1033
if ( enableSchedulerTracing ) {
889
1034
markSpawnedWork ( OffscreenLane ) ;
890
1035
}
1036
+ bubbleProperties ( workInProgress ) ;
1037
+ if ( enableProfilerTimer ) {
1038
+ if ( ( workInProgress . mode & ProfileMode ) !== NoMode ) {
1039
+ const isTimedOutSuspense = nextState !== null ;
1040
+ if ( isTimedOutSuspense ) {
1041
+ // Don't count time spent in a timed out Suspense subtree as part of the base duration.
1042
+ const primaryChildFragment = workInProgress . child ;
1043
+ if ( primaryChildFragment !== null ) {
1044
+ // $FlowFixMe Flow doens't support type casting in combiation with the -= operator
1045
+ workInProgress . treeBaseDuration -= ( ( primaryChildFragment . treeBaseDuration : any ) : number ) ;
1046
+ }
1047
+ }
1048
+ }
1049
+ }
891
1050
return null ;
892
1051
} else {
893
1052
// We should never have been in a hydration state if we didn't have a current.
@@ -904,6 +1063,20 @@ function completeWork(
904
1063
// If something suspended, schedule an effect to attach retry listeners.
905
1064
// So we might as well always mark this.
906
1065
workInProgress . effectTag |= Update ;
1066
+ bubbleProperties ( workInProgress ) ;
1067
+ if ( enableProfilerTimer ) {
1068
+ if ( ( workInProgress . mode & ProfileMode ) !== NoMode ) {
1069
+ const isTimedOutSuspense = nextState !== null ;
1070
+ if ( isTimedOutSuspense ) {
1071
+ // Don't count time spent in a timed out Suspense subtree as part of the base duration.
1072
+ const primaryChildFragment = workInProgress . child ;
1073
+ if ( primaryChildFragment !== null ) {
1074
+ // $FlowFixMe Flow doens't support type casting in combiation with the -= operator
1075
+ workInProgress . treeBaseDuration -= ( ( primaryChildFragment . treeBaseDuration : any ) : number ) ;
1076
+ }
1077
+ }
1078
+ }
1079
+ }
907
1080
return null ;
908
1081
}
909
1082
}
@@ -996,6 +1169,20 @@ function completeWork(
996
1169
// Always notify the callback
997
1170
workInProgress . effectTag |= Update ;
998
1171
}
1172
+ bubbleProperties ( workInProgress ) ;
1173
+ if ( enableProfilerTimer ) {
1174
+ if ( ( workInProgress . mode & ProfileMode ) !== NoMode ) {
1175
+ const isTimedOutSuspense = nextState !== null ;
1176
+ if ( isTimedOutSuspense ) {
1177
+ // Don't count time spent in a timed out Suspense subtree as part of the base duration.
1178
+ const primaryChildFragment = workInProgress . child ;
1179
+ if ( primaryChildFragment !== null ) {
1180
+ // $FlowFixMe Flow doens't support type casting in combiation with the -= operator
1181
+ workInProgress . treeBaseDuration -= ( ( primaryChildFragment . treeBaseDuration : any ) : number ) ;
1182
+ }
1183
+ }
1184
+ }
1185
+ }
999
1186
return null ;
1000
1187
}
1001
1188
case HostPortal :
@@ -1004,10 +1191,12 @@ function completeWork(
1004
1191
if ( current === null ) {
1005
1192
preparePortalMount ( workInProgress . stateNode . containerInfo ) ;
1006
1193
}
1194
+ bubbleProperties ( workInProgress ) ;
1007
1195
return null ;
1008
1196
case ContextProvider :
1009
1197
// Pop provider fiber
1010
1198
popProvider ( workInProgress ) ;
1199
+ bubbleProperties ( workInProgress ) ;
1011
1200
return null ;
1012
1201
case IncompleteClassComponent : {
1013
1202
// Same as class component case. I put it down here so that the tags are
@@ -1016,6 +1205,7 @@ function completeWork(
1016
1205
if ( isLegacyContextProvider ( Component ) ) {
1017
1206
popLegacyContext ( workInProgress ) ;
1018
1207
}
1208
+ bubbleProperties ( workInProgress ) ;
1019
1209
return null ;
1020
1210
}
1021
1211
case SuspenseListComponent : {
@@ -1027,6 +1217,7 @@ function completeWork(
1027
1217
if ( renderState === null ) {
1028
1218
// We're running in the default, "independent" mode.
1029
1219
// We don't do anything in this mode.
1220
+ bubbleProperties ( workInProgress ) ;
1030
1221
return null ;
1031
1222
}
1032
1223
@@ -1146,6 +1337,7 @@ function completeWork(
1146
1337
lastEffect . nextEffect = null ;
1147
1338
}
1148
1339
// We're done.
1340
+ bubbleProperties ( workInProgress ) ;
1149
1341
return null ;
1150
1342
}
1151
1343
} else if (
@@ -1231,6 +1423,7 @@ function completeWork(
1231
1423
// Do a pass over the next row.
1232
1424
return next ;
1233
1425
}
1426
+ bubbleProperties ( workInProgress ) ;
1234
1427
return null ;
1235
1428
}
1236
1429
case FundamentalComponent : {
@@ -1258,6 +1451,7 @@ function completeWork(
1258
1451
) : any ) : Instance ) ;
1259
1452
fundamentalInstance . instance = instance ;
1260
1453
if ( fundamentalImpl . reconcileChildren === false ) {
1454
+ bubbleProperties ( workInProgress ) ;
1261
1455
return null ;
1262
1456
}
1263
1457
appendAllChildren ( instance , workInProgress , false , false ) ;
@@ -1280,6 +1474,7 @@ function completeWork(
1280
1474
markUpdate ( workInProgress ) ;
1281
1475
}
1282
1476
}
1477
+ bubbleProperties ( workInProgress ) ;
1283
1478
return null ;
1284
1479
}
1285
1480
break ;
@@ -1325,31 +1520,44 @@ function completeWork(
1325
1520
markRef ( workInProgress ) ;
1326
1521
}
1327
1522
}
1523
+ bubbleProperties ( workInProgress ) ;
1328
1524
return null ;
1329
1525
}
1330
1526
break ;
1331
1527
}
1332
1528
case Block :
1333
1529
if ( enableBlocksAPI ) {
1530
+ bubbleProperties ( workInProgress ) ;
1334
1531
return null ;
1335
1532
}
1336
1533
break ;
1337
1534
case OffscreenComponent :
1338
1535
case LegacyHiddenComponent : {
1339
1536
popRenderLanes ( workInProgress ) ;
1537
+ const nextState : OffscreenState | null = workInProgress . memoizedState ;
1538
+ const nextIsHidden = nextState !== null ;
1539
+
1340
1540
if ( current !== null ) {
1341
- const nextState : OffscreenState | null = workInProgress . memoizedState ;
1342
1541
const prevState : OffscreenState | null = current . memoizedState ;
1343
1542
1344
1543
const prevIsHidden = prevState !== null ;
1345
- const nextIsHidden = nextState !== null ;
1346
1544
if (
1347
1545
prevIsHidden !== nextIsHidden &&
1348
1546
newProps . mode !== 'unstable-defer-without-hiding'
1349
1547
) {
1350
1548
workInProgress . effectTag |= Update ;
1351
1549
}
1352
1550
}
1551
+
1552
+ // Don't bubble properties for hidden children.
1553
+ if (
1554
+ ! nextIsHidden ||
1555
+ includesSomeLane ( subtreeRenderLanes , ( OffscreenLane : Lane ) ) ||
1556
+ ( workInProgress . mode & ConcurrentMode ) === NoLanes
1557
+ ) {
1558
+ bubbleProperties ( workInProgress ) ;
1559
+ }
1560
+
1353
1561
return null ;
1354
1562
}
1355
1563
}
0 commit comments