@@ -716,6 +716,11 @@ function commitLayoutEffectOnFiber(
716716 case FunctionComponent :
717717 case ForwardRef :
718718 case SimpleMemoComponent : {
719+ recursivelyTraverseLayoutEffects (
720+ finishedRoot ,
721+ finishedWork ,
722+ committedLanes ,
723+ ) ;
719724 if ( flags & Update ) {
720725 if ( ! offscreenSubtreeWasHidden ) {
721726 // At this point layout effects have already been destroyed (during mutation phase).
@@ -752,6 +757,11 @@ function commitLayoutEffectOnFiber(
752757 break ;
753758 }
754759 case ClassComponent : {
760+ recursivelyTraverseLayoutEffects (
761+ finishedRoot ,
762+ finishedWork ,
763+ committedLanes ,
764+ ) ;
755765 if ( flags & Update ) {
756766 if ( ! offscreenSubtreeWasHidden ) {
757767 const instance = finishedWork . stateNode ;
@@ -946,6 +956,11 @@ function commitLayoutEffectOnFiber(
946956 break ;
947957 }
948958 case HostRoot : {
959+ recursivelyTraverseLayoutEffects (
960+ finishedRoot ,
961+ finishedWork ,
962+ committedLanes ,
963+ ) ;
949964 if ( flags & Callback ) {
950965 // TODO: I think this is now always non-null by the time it reaches the
951966 // commit phase. Consider removing the type check.
@@ -974,6 +989,11 @@ function commitLayoutEffectOnFiber(
974989 break ;
975990 }
976991 case HostComponent : {
992+ recursivelyTraverseLayoutEffects (
993+ finishedRoot ,
994+ finishedWork ,
995+ committedLanes ,
996+ ) ;
977997 if ( flags & Update ) {
978998 const instance : Instance = finishedWork . stateNode ;
979999
@@ -1003,15 +1023,12 @@ function commitLayoutEffectOnFiber(
10031023 }
10041024 break ;
10051025 }
1006- case HostText : {
1007- // We have no life-cycles associated with text.
1008- break ;
1009- }
1010- case HostPortal : {
1011- // We have no life-cycles associated with portals.
1012- break ;
1013- }
10141026 case Profiler : {
1027+ recursivelyTraverseLayoutEffects (
1028+ finishedRoot ,
1029+ finishedWork ,
1030+ committedLanes ,
1031+ ) ;
10151032 if ( enableProfilerTimer ) {
10161033 if ( flags & Update ) {
10171034 try {
@@ -1078,6 +1095,11 @@ function commitLayoutEffectOnFiber(
10781095 break ;
10791096 }
10801097 case SuspenseComponent : {
1098+ recursivelyTraverseLayoutEffects (
1099+ finishedRoot ,
1100+ finishedWork ,
1101+ committedLanes ,
1102+ ) ;
10811103 if ( flags & Update ) {
10821104 try {
10831105 commitSuspenseHydrationCallbacks ( finishedRoot , finishedWork ) ;
@@ -1087,20 +1109,58 @@ function commitLayoutEffectOnFiber(
10871109 }
10881110 break ;
10891111 }
1090- case SuspenseListComponent :
1091- case IncompleteClassComponent :
1092- case ScopeComponent :
1093- case OffscreenComponent :
1094- case LegacyHiddenComponent :
1095- case TracingMarkerComponent : {
1112+ case OffscreenComponent : {
1113+ const isModernRoot = ( finishedWork . mode & ConcurrentMode ) !== NoMode ;
1114+ if ( isModernRoot ) {
1115+ const isHidden = finishedWork . memoizedState !== null ;
1116+ const newOffscreenSubtreeIsHidden =
1117+ isHidden || offscreenSubtreeIsHidden ;
1118+ if ( newOffscreenSubtreeIsHidden ) {
1119+ // The Offscreen tree is hidden. Skip over its layout effects.
1120+ } else {
1121+ // The Offscreen tree is visible.
1122+
1123+ const wasHidden = current !== null && current . memoizedState !== null ;
1124+ const newOffscreenSubtreeWasHidden =
1125+ wasHidden || offscreenSubtreeWasHidden ;
1126+ const prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden ;
1127+ const prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden ;
1128+ offscreenSubtreeIsHidden = newOffscreenSubtreeIsHidden ;
1129+ offscreenSubtreeWasHidden = newOffscreenSubtreeWasHidden ;
1130+
1131+ if ( offscreenSubtreeWasHidden && ! prevOffscreenSubtreeWasHidden ) {
1132+ // This is the root of a reappearing boundary. Turn its layout
1133+ // effects back on.
1134+ // TODO: Convert this to use recursion
1135+ nextEffect = finishedWork ;
1136+ reappearLayoutEffects_begin ( finishedWork ) ;
1137+ }
1138+
1139+ recursivelyTraverseLayoutEffects (
1140+ finishedRoot ,
1141+ finishedWork ,
1142+ committedLanes ,
1143+ ) ;
1144+ offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden ;
1145+ offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden ;
1146+ }
1147+ } else {
1148+ recursivelyTraverseLayoutEffects (
1149+ finishedRoot ,
1150+ finishedWork ,
1151+ committedLanes ,
1152+ ) ;
1153+ }
10961154 break ;
10971155 }
1098-
1099- default :
1100- throw new Error (
1101- 'This unit of work tag should not have side-effects. This error is ' +
1102- 'likely caused by a bug in React. Please file an issue.' ,
1156+ default : {
1157+ recursivelyTraverseLayoutEffects (
1158+ finishedRoot ,
1159+ finishedWork ,
1160+ committedLanes ,
11031161 ) ;
1162+ break ;
1163+ }
11041164 }
11051165}
11061166
@@ -2591,112 +2651,30 @@ export function commitLayoutEffects(
25912651) : void {
25922652 inProgressLanes = committedLanes ;
25932653 inProgressRoot = root ;
2594- nextEffect = finishedWork ;
25952654
2596- commitLayoutEffects_begin ( finishedWork , root , committedLanes ) ;
2655+ const current = finishedWork . alternate ;
2656+ commitLayoutEffectOnFiber ( root , current , finishedWork , committedLanes ) ;
25972657
25982658 inProgressLanes = null ;
25992659 inProgressRoot = null ;
26002660}
26012661
2602- function commitLayoutEffects_begin (
2603- subtreeRoot : Fiber ,
2662+ function recursivelyTraverseLayoutEffects (
26042663 root : FiberRoot ,
2605- committedLanes : Lanes ,
2606- ) {
2607- // Suspense layout effects semantics don't change for legacy roots.
2608- const isModernRoot = ( subtreeRoot . mode & ConcurrentMode ) !== NoMode ;
2609-
2610- while ( nextEffect !== null ) {
2611- const fiber = nextEffect ;
2612- const firstChild = fiber . child ;
2613-
2614- if ( fiber . tag === OffscreenComponent && isModernRoot ) {
2615- // Keep track of the current Offscreen stack's state.
2616- const isHidden = fiber . memoizedState !== null ;
2617- const newOffscreenSubtreeIsHidden = isHidden || offscreenSubtreeIsHidden ;
2618- if ( newOffscreenSubtreeIsHidden ) {
2619- // The Offscreen tree is hidden. Skip over its layout effects.
2620- commitLayoutMountEffects_complete ( subtreeRoot , root , committedLanes ) ;
2621- continue ;
2622- } else {
2623- // TODO (Offscreen) Also check: subtreeFlags & LayoutMask
2624- const current = fiber . alternate ;
2625- const wasHidden = current !== null && current . memoizedState !== null ;
2626- const newOffscreenSubtreeWasHidden =
2627- wasHidden || offscreenSubtreeWasHidden ;
2628- const prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden ;
2629- const prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden ;
2630-
2631- // Traverse the Offscreen subtree with the current Offscreen as the root.
2632- offscreenSubtreeIsHidden = newOffscreenSubtreeIsHidden ;
2633- offscreenSubtreeWasHidden = newOffscreenSubtreeWasHidden ;
2634-
2635- if ( offscreenSubtreeWasHidden && ! prevOffscreenSubtreeWasHidden ) {
2636- // This is the root of a reappearing boundary. Turn its layout effects
2637- // back on.
2638- nextEffect = fiber ;
2639- reappearLayoutEffects_begin ( fiber ) ;
2640- }
2641-
2642- let child = firstChild ;
2643- while ( child !== null ) {
2644- nextEffect = child ;
2645- commitLayoutEffects_begin (
2646- child , // New root; bubble back up to here and stop.
2647- root ,
2648- committedLanes ,
2649- ) ;
2650- child = child . sibling ;
2651- }
2652-
2653- // Restore Offscreen state and resume in our-progress traversal.
2654- nextEffect = fiber ;
2655- offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden ;
2656- offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden ;
2657- commitLayoutMountEffects_complete ( subtreeRoot , root , committedLanes ) ;
2658-
2659- continue ;
2660- }
2661- }
2662-
2663- if ( ( fiber . subtreeFlags & LayoutMask ) !== NoFlags && firstChild !== null ) {
2664- firstChild . return = fiber ;
2665- nextEffect = firstChild ;
2666- } else {
2667- commitLayoutMountEffects_complete ( subtreeRoot , root , committedLanes ) ;
2668- }
2669- }
2670- }
2671-
2672- function commitLayoutMountEffects_complete (
2673- subtreeRoot : Fiber ,
2674- root : FiberRoot ,
2675- committedLanes : Lanes ,
2664+ parentFiber : Fiber ,
2665+ lanes : Lanes ,
26762666) {
2677- while ( nextEffect !== null ) {
2678- const fiber = nextEffect ;
2679- if ( ( fiber . flags & LayoutMask ) !== NoFlags ) {
2680- const current = fiber . alternate ;
2681- setCurrentDebugFiberInDEV ( fiber ) ;
2682- commitLayoutEffectOnFiber ( root , current , fiber , committedLanes ) ;
2683- resetCurrentDebugFiberInDEV ( ) ;
2684- }
2685-
2686- if ( fiber === subtreeRoot ) {
2687- nextEffect = null ;
2688- return ;
2689- }
2690-
2691- const sibling = fiber . sibling ;
2692- if ( sibling !== null ) {
2693- sibling . return = fiber . return ;
2694- nextEffect = sibling ;
2695- return ;
2667+ const prevDebugFiber = getCurrentDebugFiberInDEV ( ) ;
2668+ if ( parentFiber . subtreeFlags & LayoutMask ) {
2669+ let child = parentFiber . child ;
2670+ while ( child !== null ) {
2671+ setCurrentDebugFiberInDEV ( child ) ;
2672+ const current = child . alternate ;
2673+ commitLayoutEffectOnFiber ( root , current , child , lanes ) ;
2674+ child = child . sibling ;
26962675 }
2697-
2698- nextEffect = fiber . return ;
26992676 }
2677+ setCurrentDebugFiberInDEV ( prevDebugFiber ) ;
27002678}
27012679
27022680function disappearLayoutEffects_begin ( subtreeRoot : Fiber ) {
0 commit comments