@@ -716,6 +716,11 @@ function commitLayoutEffectOnFiber(
716
716
case FunctionComponent :
717
717
case ForwardRef :
718
718
case SimpleMemoComponent : {
719
+ recursivelyTraverseLayoutEffects (
720
+ finishedRoot ,
721
+ finishedWork ,
722
+ committedLanes ,
723
+ ) ;
719
724
if ( flags & Update ) {
720
725
if ( ! offscreenSubtreeWasHidden ) {
721
726
// At this point layout effects have already been destroyed (during mutation phase).
@@ -752,6 +757,11 @@ function commitLayoutEffectOnFiber(
752
757
break ;
753
758
}
754
759
case ClassComponent : {
760
+ recursivelyTraverseLayoutEffects (
761
+ finishedRoot ,
762
+ finishedWork ,
763
+ committedLanes ,
764
+ ) ;
755
765
if ( flags & Update ) {
756
766
if ( ! offscreenSubtreeWasHidden ) {
757
767
const instance = finishedWork . stateNode ;
@@ -946,6 +956,11 @@ function commitLayoutEffectOnFiber(
946
956
break ;
947
957
}
948
958
case HostRoot : {
959
+ recursivelyTraverseLayoutEffects (
960
+ finishedRoot ,
961
+ finishedWork ,
962
+ committedLanes ,
963
+ ) ;
949
964
if ( flags & Callback ) {
950
965
// TODO: I think this is now always non-null by the time it reaches the
951
966
// commit phase. Consider removing the type check.
@@ -974,6 +989,11 @@ function commitLayoutEffectOnFiber(
974
989
break ;
975
990
}
976
991
case HostComponent : {
992
+ recursivelyTraverseLayoutEffects (
993
+ finishedRoot ,
994
+ finishedWork ,
995
+ committedLanes ,
996
+ ) ;
977
997
if ( flags & Update ) {
978
998
const instance : Instance = finishedWork . stateNode ;
979
999
@@ -1003,15 +1023,12 @@ function commitLayoutEffectOnFiber(
1003
1023
}
1004
1024
break ;
1005
1025
}
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
- }
1014
1026
case Profiler : {
1027
+ recursivelyTraverseLayoutEffects (
1028
+ finishedRoot ,
1029
+ finishedWork ,
1030
+ committedLanes ,
1031
+ ) ;
1015
1032
if ( enableProfilerTimer ) {
1016
1033
if ( flags & Update ) {
1017
1034
try {
@@ -1078,6 +1095,11 @@ function commitLayoutEffectOnFiber(
1078
1095
break ;
1079
1096
}
1080
1097
case SuspenseComponent : {
1098
+ recursivelyTraverseLayoutEffects (
1099
+ finishedRoot ,
1100
+ finishedWork ,
1101
+ committedLanes ,
1102
+ ) ;
1081
1103
if ( flags & Update ) {
1082
1104
try {
1083
1105
commitSuspenseHydrationCallbacks ( finishedRoot , finishedWork ) ;
@@ -1087,20 +1109,58 @@ function commitLayoutEffectOnFiber(
1087
1109
}
1088
1110
break ;
1089
1111
}
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
+ }
1096
1154
break ;
1097
1155
}
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 ,
1103
1161
) ;
1162
+ break ;
1163
+ }
1104
1164
}
1105
1165
}
1106
1166
@@ -2591,112 +2651,30 @@ export function commitLayoutEffects(
2591
2651
) : void {
2592
2652
inProgressLanes = committedLanes ;
2593
2653
inProgressRoot = root ;
2594
- nextEffect = finishedWork ;
2595
2654
2596
- commitLayoutEffects_begin ( finishedWork , root , committedLanes ) ;
2655
+ const current = finishedWork . alternate ;
2656
+ commitLayoutEffectOnFiber ( root , current , finishedWork , committedLanes ) ;
2597
2657
2598
2658
inProgressLanes = null ;
2599
2659
inProgressRoot = null ;
2600
2660
}
2601
2661
2602
- function commitLayoutEffects_begin (
2603
- subtreeRoot : Fiber ,
2662
+ function recursivelyTraverseLayoutEffects (
2604
2663
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 ,
2676
2666
) {
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 ;
2696
2675
}
2697
-
2698
- nextEffect = fiber . return ;
2699
2676
}
2677
+ setCurrentDebugFiberInDEV ( prevDebugFiber ) ;
2700
2678
}
2701
2679
2702
2680
function disappearLayoutEffects_begin ( subtreeRoot : Fiber ) {
0 commit comments