@@ -225,18 +225,6 @@ const callComponentWillUnmountWithTimer = function(current, instance) {
225
225
}
226
226
} ;
227
227
228
- // Capture errors so they don't interrupt mounting.
229
- function safelyCallCommitHookLayoutEffectListMount (
230
- current : Fiber ,
231
- nearestMountedAncestor : Fiber | null ,
232
- ) {
233
- try {
234
- commitHookEffectListMount ( HookLayout , current ) ;
235
- } catch ( error ) {
236
- captureCommitPhaseError ( current , nearestMountedAncestor , error ) ;
237
- }
238
- }
239
-
240
228
// Capture errors so they don't interrupt unmounting.
241
229
function safelyCallComponentWillUnmount (
242
230
current : Fiber ,
@@ -250,19 +238,6 @@ function safelyCallComponentWillUnmount(
250
238
}
251
239
}
252
240
253
- // Capture errors so they don't interrupt mounting.
254
- function safelyCallComponentDidMount (
255
- current : Fiber ,
256
- nearestMountedAncestor : Fiber | null ,
257
- instance : any ,
258
- ) {
259
- try {
260
- instance . componentDidMount ( ) ;
261
- } catch ( error ) {
262
- captureCommitPhaseError ( current , nearestMountedAncestor , error ) ;
263
- }
264
- }
265
-
266
241
// Capture errors so they don't interrupt mounting.
267
242
function safelyAttachRef ( current : Fiber , nearestMountedAncestor : Fiber | null ) {
268
243
try {
@@ -706,7 +681,7 @@ export function commitPassiveEffectDurations(
706
681
}
707
682
}
708
683
709
- function commitHookLayoutEffects ( finishedWork : Fiber ) {
684
+ function commitHookLayoutEffects ( finishedWork : Fiber , hookFlags : HookFlags ) {
710
685
// At this point layout effects have already been destroyed (during mutation phase).
711
686
// This is done to prevent sibling component effects from interfering with each other,
712
687
// e.g. a destroy function in one component should never override a ref set
@@ -718,14 +693,14 @@ function commitHookLayoutEffects(finishedWork: Fiber) {
718
693
) {
719
694
try {
720
695
startLayoutEffectTimer ( ) ;
721
- commitHookEffectListMount ( HookLayout | HookHasEffect , finishedWork ) ;
696
+ commitHookEffectListMount ( hookFlags , finishedWork ) ;
722
697
} catch ( error ) {
723
698
captureCommitPhaseError ( finishedWork , finishedWork . return , error ) ;
724
699
}
725
700
recordLayoutEffectDuration ( finishedWork ) ;
726
701
} else {
727
702
try {
728
- commitHookEffectListMount ( HookLayout | HookHasEffect , finishedWork ) ;
703
+ commitHookEffectListMount ( hookFlags , finishedWork ) ;
729
704
} catch ( error ) {
730
705
captureCommitPhaseError ( finishedWork , finishedWork . return , error ) ;
731
706
}
@@ -853,7 +828,7 @@ function commitClassLayoutLifecycles(
853
828
}
854
829
}
855
830
856
- function commitClassCallbacks ( finishedWork : Fiber , current : Fiber | null ) {
831
+ function commitClassCallbacks ( finishedWork : Fiber ) {
857
832
// TODO: I think this is now always non-null by the time it reaches the
858
833
// commit phase. Consider removing the type check.
859
834
const updateQueue : UpdateQueue < * > | null = ( finishedWork . updateQueue : any ) ;
@@ -897,7 +872,7 @@ function commitClassCallbacks(finishedWork: Fiber, current: Fiber | null) {
897
872
}
898
873
}
899
874
900
- function commitHostComponentMount ( finishedWork : Fiber , current : Fiber | null ) {
875
+ function commitHostComponentMount ( finishedWork : Fiber ) {
901
876
const type = finishedWork . type ;
902
877
const props = finishedWork . memoizedProps ;
903
878
const instance : Instance = finishedWork . stateNode ;
@@ -978,6 +953,8 @@ function commitLayoutEffectOnFiber(
978
953
finishedWork : Fiber ,
979
954
committedLanes : Lanes ,
980
955
) : void {
956
+ // When updating this function, also update reappearLayoutEffects, which does
957
+ // most of the same things when an offscreen tree goes from hidden -> visible.
981
958
const flags = finishedWork . flags ;
982
959
switch ( finishedWork . tag ) {
983
960
case FunctionComponent :
@@ -989,9 +966,7 @@ function commitLayoutEffectOnFiber(
989
966
committedLanes ,
990
967
) ;
991
968
if ( flags & Update ) {
992
- if ( ! offscreenSubtreeWasHidden ) {
993
- commitHookLayoutEffects ( finishedWork ) ;
994
- }
969
+ commitHookLayoutEffects ( finishedWork , HookLayout | HookHasEffect ) ;
995
970
}
996
971
break ;
997
972
}
@@ -1002,19 +977,15 @@ function commitLayoutEffectOnFiber(
1002
977
committedLanes ,
1003
978
) ;
1004
979
if ( flags & Update ) {
1005
- if ( ! offscreenSubtreeWasHidden ) {
1006
- commitClassLayoutLifecycles ( finishedWork , current ) ;
1007
- }
980
+ commitClassLayoutLifecycles ( finishedWork , current ) ;
1008
981
}
1009
982
1010
983
if ( flags & Callback ) {
1011
- commitClassCallbacks ( finishedWork , current ) ;
984
+ commitClassCallbacks ( finishedWork ) ;
1012
985
}
1013
986
1014
987
if ( flags & Ref ) {
1015
- if ( ! offscreenSubtreeWasHidden ) {
1016
- safelyAttachRef ( finishedWork , finishedWork . return ) ;
1017
- }
988
+ safelyAttachRef ( finishedWork , finishedWork . return ) ;
1018
989
}
1019
990
break ;
1020
991
}
@@ -1063,13 +1034,11 @@ function commitLayoutEffectOnFiber(
1063
1034
// These effects should only be committed when components are first mounted,
1064
1035
// aka when there is no current/alternate.
1065
1036
if ( current === null && flags & Update ) {
1066
- commitHostComponentMount ( finishedWork , current ) ;
1037
+ commitHostComponentMount ( finishedWork ) ;
1067
1038
}
1068
1039
1069
1040
if ( flags & Ref ) {
1070
- if ( ! offscreenSubtreeWasHidden ) {
1071
- safelyAttachRef ( finishedWork , finishedWork . return ) ;
1072
- }
1041
+ safelyAttachRef ( finishedWork , finishedWork . return ) ;
1073
1042
}
1074
1043
break ;
1075
1044
}
@@ -1117,19 +1086,25 @@ function commitLayoutEffectOnFiber(
1117
1086
offscreenSubtreeWasHidden = newOffscreenSubtreeWasHidden ;
1118
1087
1119
1088
if ( offscreenSubtreeWasHidden && ! prevOffscreenSubtreeWasHidden ) {
1120
- // This is the root of a reappearing boundary. Turn its layout
1121
- // effects back on.
1122
- recursivelyTraverseReappearLayoutEffects ( finishedWork ) ;
1089
+ // This is the root of a reappearing boundary. As we continue
1090
+ // traversing the layout effects, we must also re-mount layout
1091
+ // effects that were unmounted when the Offscreen subtree was
1092
+ // hidden. So this is a superset of the normal commitLayoutEffects.
1093
+ const includeWorkInProgressEffects =
1094
+ ( finishedWork . subtreeFlags & LayoutMask ) !== NoFlags ;
1095
+ recursivelyTraverseReappearLayoutEffects (
1096
+ finishedRoot ,
1097
+ finishedWork ,
1098
+ committedLanes ,
1099
+ includeWorkInProgressEffects ,
1100
+ ) ;
1101
+ } else {
1102
+ recursivelyTraverseLayoutEffects (
1103
+ finishedRoot ,
1104
+ finishedWork ,
1105
+ committedLanes ,
1106
+ ) ;
1123
1107
}
1124
-
1125
- // TODO: We shouldn't traverse twice when reappearing layout effects.
1126
- // Move this into the else block of the above if statement, and modify
1127
- // reappearLayoutEffects to fire regular layout effects, too.
1128
- recursivelyTraverseLayoutEffects (
1129
- finishedRoot ,
1130
- finishedWork ,
1131
- committedLanes ,
1132
- ) ;
1133
1108
offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden ;
1134
1109
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden ;
1135
1110
}
@@ -2723,90 +2698,177 @@ function recursivelyTraverseDisappearLayoutEffects(parentFiber: Fiber) {
2723
2698
}
2724
2699
}
2725
2700
2726
- function reappearLayoutEffects ( finishedWork : Fiber ) {
2701
+ function reappearLayoutEffects (
2702
+ finishedRoot : FiberRoot ,
2703
+ current : Fiber | null ,
2704
+ finishedWork : Fiber ,
2705
+ committedLanes : Lanes ,
2706
+ // This function visits both newly finished work and nodes that were re-used
2707
+ // from a previously committed tree. We cannot check non-static flags if the
2708
+ // node was reused.
2709
+ includeWorkInProgressEffects : boolean ,
2710
+ ) {
2727
2711
// Turn on layout effects in a tree that previously disappeared.
2728
- // TODO (Offscreen) Check: flags & LayoutStatic
2712
+ const flags = finishedWork . flags ;
2729
2713
switch ( finishedWork . tag ) {
2730
2714
case FunctionComponent :
2731
2715
case ForwardRef :
2732
2716
case SimpleMemoComponent : {
2733
- recursivelyTraverseReappearLayoutEffects ( finishedWork ) ;
2734
-
2735
- // TODO: Check for LayoutStatic flag
2736
- if (
2737
- enableProfilerTimer &&
2738
- enableProfilerCommitHooks &&
2739
- finishedWork . mode & ProfileMode
2740
- ) {
2741
- try {
2742
- startLayoutEffectTimer ( ) ;
2743
- safelyCallCommitHookLayoutEffectListMount (
2744
- finishedWork ,
2745
- finishedWork . return ,
2746
- ) ;
2747
- } finally {
2748
- recordLayoutEffectDuration ( finishedWork ) ;
2749
- }
2750
- } else {
2751
- safelyCallCommitHookLayoutEffectListMount (
2752
- finishedWork ,
2753
- finishedWork . return ,
2754
- ) ;
2755
- }
2717
+ recursivelyTraverseReappearLayoutEffects (
2718
+ finishedRoot ,
2719
+ finishedWork ,
2720
+ committedLanes ,
2721
+ includeWorkInProgressEffects ,
2722
+ ) ;
2723
+ // TODO: Check flags & LayoutStatic
2724
+ commitHookLayoutEffects ( finishedWork , HookLayout ) ;
2756
2725
break ;
2757
2726
}
2758
2727
case ClassComponent : {
2759
- recursivelyTraverseReappearLayoutEffects ( finishedWork ) ;
2728
+ recursivelyTraverseReappearLayoutEffects (
2729
+ finishedRoot ,
2730
+ finishedWork ,
2731
+ committedLanes ,
2732
+ includeWorkInProgressEffects ,
2733
+ ) ;
2760
2734
2761
- const instance = finishedWork . stateNode ;
2762
2735
// TODO: Check for LayoutStatic flag
2736
+ const instance = finishedWork . stateNode ;
2763
2737
if ( typeof instance . componentDidMount === 'function' ) {
2764
- safelyCallComponentDidMount (
2765
- finishedWork ,
2766
- finishedWork . return ,
2767
- instance ,
2768
- ) ;
2738
+ try {
2739
+ instance . componentDidMount ( ) ;
2740
+ } catch ( error ) {
2741
+ captureCommitPhaseError ( finishedWork , finishedWork . return , error ) ;
2742
+ }
2769
2743
}
2770
- // TODO: Check for RefStatic flag
2771
- safelyAttachRef ( finishedWork , finishedWork . return ) ;
2744
+
2745
+ // Commit any callbacks that would have fired while the component
2746
+ // was hidden.
2772
2747
const updateQueue : UpdateQueue <
2773
2748
* ,
2774
2749
> | null = ( finishedWork . updateQueue : any ) ;
2775
2750
if ( updateQueue !== null ) {
2776
2751
commitHiddenCallbacks ( updateQueue , instance ) ;
2777
2752
}
2753
+
2754
+ // If this is newly finished work, check for setState callbacks
2755
+ if ( includeWorkInProgressEffects && flags & Callback ) {
2756
+ commitClassCallbacks ( finishedWork ) ;
2757
+ }
2758
+
2759
+ // TODO: Check flags & RefStatic
2760
+ safelyAttachRef ( finishedWork , finishedWork . return ) ;
2778
2761
break ;
2779
2762
}
2763
+ // Unlike commitLayoutEffectsOnFiber, we don't need to handle HostRoot
2764
+ // because this function only visits nodes that are inside an
2765
+ // Offscreen fiber.
2766
+ // case HostRoot: {
2767
+ // ...
2768
+ // }
2780
2769
case HostComponent : {
2781
- recursivelyTraverseReappearLayoutEffects ( finishedWork ) ;
2770
+ recursivelyTraverseReappearLayoutEffects (
2771
+ finishedRoot ,
2772
+ finishedWork ,
2773
+ committedLanes ,
2774
+ includeWorkInProgressEffects ,
2775
+ ) ;
2782
2776
2783
- // TODO: Check for RefStatic flag
2777
+ // Renderers may schedule work to be done after host components are mounted
2778
+ // (eg DOM renderer may schedule auto-focus for inputs and form controls).
2779
+ // These effects should only be committed when components are first mounted,
2780
+ // aka when there is no current/alternate.
2781
+ if ( includeWorkInProgressEffects && current === null && flags & Update ) {
2782
+ commitHostComponentMount ( finishedWork ) ;
2783
+ }
2784
+
2785
+ // TODO: Check flags & Ref
2784
2786
safelyAttachRef ( finishedWork , finishedWork . return ) ;
2785
2787
break ;
2786
2788
}
2789
+ case Profiler : {
2790
+ recursivelyTraverseReappearLayoutEffects (
2791
+ finishedRoot ,
2792
+ finishedWork ,
2793
+ committedLanes ,
2794
+ includeWorkInProgressEffects ,
2795
+ ) ;
2796
+ // TODO: Figure out how Profiler updates should work with Offscreen
2797
+ if ( includeWorkInProgressEffects && flags & Update ) {
2798
+ commitProfilerUpdate ( finishedWork , current ) ;
2799
+ }
2800
+ break ;
2801
+ }
2802
+ case SuspenseComponent : {
2803
+ recursivelyTraverseReappearLayoutEffects (
2804
+ finishedRoot ,
2805
+ finishedWork ,
2806
+ committedLanes ,
2807
+ includeWorkInProgressEffects ,
2808
+ ) ;
2809
+
2810
+ // TODO: Figure out how Suspense hydration callbacks should work
2811
+ // with Offscreen.
2812
+ if ( includeWorkInProgressEffects && flags & Update ) {
2813
+ commitSuspenseHydrationCallbacks ( finishedRoot , finishedWork ) ;
2814
+ }
2815
+ break ;
2816
+ }
2787
2817
case OffscreenComponent : {
2788
- const isHidden = finishedWork . memoizedState !== null ;
2818
+ const offscreenState : OffscreenState = finishedWork . memoizedState ;
2819
+ const isHidden = offscreenState !== null ;
2789
2820
if ( isHidden ) {
2790
2821
// Nested Offscreen tree is still hidden. Don't re-appear its effects.
2791
2822
} else {
2792
- recursivelyTraverseReappearLayoutEffects ( finishedWork ) ;
2823
+ recursivelyTraverseReappearLayoutEffects (
2824
+ finishedRoot ,
2825
+ finishedWork ,
2826
+ committedLanes ,
2827
+ includeWorkInProgressEffects ,
2828
+ ) ;
2793
2829
}
2794
2830
break ;
2795
2831
}
2796
2832
default : {
2797
- recursivelyTraverseReappearLayoutEffects ( finishedWork ) ;
2833
+ recursivelyTraverseReappearLayoutEffects (
2834
+ finishedRoot ,
2835
+ finishedWork ,
2836
+ committedLanes ,
2837
+ includeWorkInProgressEffects ,
2838
+ ) ;
2798
2839
break ;
2799
2840
}
2800
2841
}
2801
2842
}
2802
2843
2803
- function recursivelyTraverseReappearLayoutEffects ( parentFiber : Fiber ) {
2844
+ function recursivelyTraverseReappearLayoutEffects (
2845
+ finishedRoot : FiberRoot ,
2846
+ parentFiber : Fiber ,
2847
+ committedLanes : Lanes ,
2848
+ includeWorkInProgressEffects : boolean ,
2849
+ ) {
2850
+ // This function visits both newly finished work and nodes that were re-used
2851
+ // from a previously committed tree. We cannot check non-static flags if the
2852
+ // node was reused.
2853
+ const childShouldIncludeWorkInProgressEffects =
2854
+ includeWorkInProgressEffects &&
2855
+ ( parentFiber . subtreeFlags & LayoutMask ) !== NoFlags ;
2856
+
2804
2857
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
2858
+ const prevDebugFiber = getCurrentDebugFiberInDEV ( ) ;
2805
2859
let child = parentFiber . child ;
2806
2860
while ( child !== null ) {
2807
- reappearLayoutEffects ( child ) ;
2861
+ const current = child . alternate ;
2862
+ reappearLayoutEffects (
2863
+ finishedRoot ,
2864
+ current ,
2865
+ child ,
2866
+ committedLanes ,
2867
+ childShouldIncludeWorkInProgressEffects ,
2868
+ ) ;
2808
2869
child = child . sibling ;
2809
2870
}
2871
+ setCurrentDebugFiberInDEV ( prevDebugFiber ) ;
2810
2872
}
2811
2873
2812
2874
export function commitPassiveMountEffects (
0 commit comments