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,14 +58,20 @@ 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
+ ConcurrentMode ,
65
+ ProfileMode ,
66
+ } from './ReactTypeOfMode' ;
62
67
import {
63
68
Ref ,
64
69
Update ,
65
70
NoFlags ,
66
71
DidCapture ,
67
72
Snapshot ,
68
73
MutationMask ,
74
+ StaticMask ,
69
75
} from './ReactFiberFlags' ;
70
76
import invariant from 'shared/invariant' ;
71
77
@@ -137,9 +143,16 @@ import {
137
143
renderHasNotSuspendedYet ,
138
144
popRenderLanes ,
139
145
getRenderTargetTime ,
146
+ subtreeRenderLanes ,
140
147
} from './ReactFiberWorkLoop.new' ;
141
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' ;
143
156
import { resetChildFibers } from './ReactChildFiber.new' ;
144
157
import { createScopeInstance } from './ReactFiberScope.new' ;
145
158
import { transferActualDuration } from './ReactProfilerTimer.new' ;
@@ -668,6 +681,114 @@ function cutOffTailIfNeeded(
668
681
}
669
682
}
670
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
+
671
792
function completeWork (
672
793
current : Fiber | null ,
673
794
workInProgress : Fiber ,
@@ -686,12 +807,14 @@ function completeWork(
686
807
case Profiler :
687
808
case ContextConsumer :
688
809
case MemoComponent :
810
+ bubbleProperties ( workInProgress ) ;
689
811
return null ;
690
812
case ClassComponent : {
691
813
const Component = workInProgress . type ;
692
814
if ( isLegacyContextProvider ( Component ) ) {
693
815
popLegacyContext ( workInProgress ) ;
694
816
}
817
+ bubbleProperties ( workInProgress ) ;
695
818
return null ;
696
819
}
697
820
case HostRoot : {
@@ -720,6 +843,7 @@ function completeWork(
720
843
}
721
844
}
722
845
updateHostContainer ( current , workInProgress ) ;
846
+ bubbleProperties ( workInProgress ) ;
723
847
return null ;
724
848
}
725
849
case HostComponent : {
@@ -746,6 +870,7 @@ function completeWork(
746
870
'caused by a bug in React. Please file an issue.' ,
747
871
) ;
748
872
// This can happen when we abort work.
873
+ bubbleProperties ( workInProgress ) ;
749
874
return null ;
750
875
}
751
876
@@ -803,6 +928,7 @@ function completeWork(
803
928
markRef ( workInProgress ) ;
804
929
}
805
930
}
931
+ bubbleProperties ( workInProgress ) ;
806
932
return null ;
807
933
}
808
934
case HostText : {
@@ -837,6 +963,7 @@ function completeWork(
837
963
) ;
838
964
}
839
965
}
966
+ bubbleProperties ( workInProgress ) ;
840
967
return null ;
841
968
}
842
969
case SuspenseComponent : {
@@ -856,6 +983,20 @@ function completeWork(
856
983
if ( enableSchedulerTracing ) {
857
984
markSpawnedWork ( OffscreenLane ) ;
858
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
+ }
859
1000
return null ;
860
1001
} else {
861
1002
// We should never have been in a hydration state if we didn't have a current.
@@ -872,6 +1013,20 @@ function completeWork(
872
1013
// If something suspended, schedule an effect to attach retry listeners.
873
1014
// So we might as well always mark this.
874
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
+ }
875
1030
return null ;
876
1031
}
877
1032
}
@@ -887,6 +1042,7 @@ function completeWork(
887
1042
) {
888
1043
transferActualDuration ( workInProgress ) ;
889
1044
}
1045
+ // Don't bubble properties in this case.
890
1046
return workInProgress ;
891
1047
}
892
1048
@@ -964,6 +1120,19 @@ function completeWork(
964
1120
// Always notify the callback
965
1121
workInProgress . flags |= Update ;
966
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
+ }
967
1136
return null ;
968
1137
}
969
1138
case HostPortal :
@@ -972,10 +1141,12 @@ function completeWork(
972
1141
if ( current === null ) {
973
1142
preparePortalMount ( workInProgress . stateNode . containerInfo ) ;
974
1143
}
1144
+ bubbleProperties ( workInProgress ) ;
975
1145
return null ;
976
1146
case ContextProvider :
977
1147
// Pop provider fiber
978
1148
popProvider ( workInProgress ) ;
1149
+ bubbleProperties ( workInProgress ) ;
979
1150
return null ;
980
1151
case IncompleteClassComponent : {
981
1152
// Same as class component case. I put it down here so that the tags are
@@ -984,6 +1155,7 @@ function completeWork(
984
1155
if ( isLegacyContextProvider ( Component ) ) {
985
1156
popLegacyContext ( workInProgress ) ;
986
1157
}
1158
+ bubbleProperties ( workInProgress ) ;
987
1159
return null ;
988
1160
}
989
1161
case SuspenseListComponent : {
@@ -995,6 +1167,7 @@ function completeWork(
995
1167
if ( renderState === null ) {
996
1168
// We're running in the default, "independent" mode.
997
1169
// We don't do anything in this mode.
1170
+ bubbleProperties ( workInProgress ) ;
998
1171
return null ;
999
1172
}
1000
1173
@@ -1060,6 +1233,7 @@ function completeWork(
1060
1233
ForceSuspenseFallback ,
1061
1234
) ,
1062
1235
) ;
1236
+ // Don't bubble properties in this case.
1063
1237
return workInProgress . child ;
1064
1238
}
1065
1239
row = row . sibling ;
@@ -1117,6 +1291,7 @@ function completeWork(
1117
1291
! getIsHydrating ( ) // We don't cut it if we're hydrating.
1118
1292
) {
1119
1293
// We're done.
1294
+ bubbleProperties ( workInProgress ) ;
1120
1295
return null ;
1121
1296
}
1122
1297
} else if (
@@ -1188,8 +1363,10 @@ function completeWork(
1188
1363
}
1189
1364
pushSuspenseContext ( workInProgress , suspenseContext ) ;
1190
1365
// Do a pass over the next row.
1366
+ // Don't bubble properties in this case.
1191
1367
return next ;
1192
1368
}
1369
+ bubbleProperties ( workInProgress ) ;
1193
1370
return null ;
1194
1371
}
1195
1372
case FundamentalComponent : {
@@ -1217,6 +1394,7 @@ function completeWork(
1217
1394
) : any ) : Instance ) ;
1218
1395
fundamentalInstance . instance = instance ;
1219
1396
if ( fundamentalImpl . reconcileChildren === false ) {
1397
+ bubbleProperties ( workInProgress ) ;
1220
1398
return null ;
1221
1399
}
1222
1400
appendAllChildren ( instance , workInProgress , false , false ) ;
@@ -1239,6 +1417,7 @@ function completeWork(
1239
1417
markUpdate ( workInProgress ) ;
1240
1418
}
1241
1419
}
1420
+ bubbleProperties ( workInProgress ) ;
1242
1421
return null ;
1243
1422
}
1244
1423
break ;
@@ -1261,31 +1440,44 @@ function completeWork(
1261
1440
markRef ( workInProgress ) ;
1262
1441
}
1263
1442
}
1443
+ bubbleProperties ( workInProgress ) ;
1264
1444
return null ;
1265
1445
}
1266
1446
break ;
1267
1447
}
1268
1448
case Block :
1269
1449
if ( enableBlocksAPI ) {
1450
+ bubbleProperties ( workInProgress ) ;
1270
1451
return null ;
1271
1452
}
1272
1453
break ;
1273
1454
case OffscreenComponent :
1274
1455
case LegacyHiddenComponent : {
1275
1456
popRenderLanes ( workInProgress ) ;
1457
+ const nextState : OffscreenState | null = workInProgress . memoizedState ;
1458
+ const nextIsHidden = nextState !== null ;
1459
+
1276
1460
if ( current !== null ) {
1277
- const nextState : OffscreenState | null = workInProgress . memoizedState ;
1278
1461
const prevState : OffscreenState | null = current . memoizedState ;
1279
1462
1280
1463
const prevIsHidden = prevState !== null ;
1281
- const nextIsHidden = nextState !== null ;
1282
1464
if (
1283
1465
prevIsHidden !== nextIsHidden &&
1284
1466
newProps . mode !== 'unstable-defer-without-hiding'
1285
1467
) {
1286
1468
workInProgress . flags |= Update ;
1287
1469
}
1288
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
+
1289
1481
return null ;
1290
1482
}
1291
1483
}
0 commit comments