88 */
99
1010import type { Fiber } from './ReactInternalTypes' ;
11- import type { Lanes } from './ReactFiberLane' ;
11+ import type { Lanes , Lane } from './ReactFiberLane' ;
1212import 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' ;
6267import {
6368 Ref ,
6469 Update ,
6570 NoFlags ,
6671 DidCapture ,
6772 Snapshot ,
6873 MutationMask ,
74+ StaticMask ,
6975} from './ReactFiberFlags' ;
7076import invariant from 'shared/invariant' ;
7177
@@ -137,9 +143,16 @@ import {
137143 renderHasNotSuspendedYet ,
138144 popRenderLanes ,
139145 getRenderTargetTime ,
146+ subtreeRenderLanes ,
140147} from './ReactFiberWorkLoop.new' ;
141148import { 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' ;
143156import { resetChildFibers } from './ReactChildFiber.new' ;
144157import { createScopeInstance } from './ReactFiberScope.new' ;
145158import { 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+
671792function 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