@@ -32,6 +32,7 @@ import {
32
32
enableDebugTracing ,
33
33
enableSchedulingProfiler ,
34
34
enableScopeAPI ,
35
+ enableDoubleInvokingEffects ,
35
36
} from 'shared/ReactFeatureFlags' ;
36
37
import ReactSharedInternals from 'shared/ReactSharedInternals' ;
37
38
import invariant from 'shared/invariant' ;
@@ -55,6 +56,7 @@ import {
55
56
NoEffect as NoHookEffect ,
56
57
HasEffect as HookHasEffect ,
57
58
Passive as HookPassive ,
59
+ Layout as HookLayout ,
58
60
} from './ReactHookEffectTags' ;
59
61
import {
60
62
logCommitStarted ,
@@ -208,9 +210,12 @@ import {
208
210
commitDeletion ,
209
211
commitDetachRef ,
210
212
commitAttachRef ,
213
+ commitHookEffectListUnmount ,
214
+ commitHookEffectListMount ,
211
215
commitPassiveEffectDurations ,
212
216
commitResetTextContent ,
213
217
isSuspenseBoundaryBeingHidden ,
218
+ safelyCallComponentWillUnmount ,
214
219
safelyCallDestroy ,
215
220
} from './ReactFiberCommitWork.new' ;
216
221
import { enqueueUpdate } from './ReactUpdateQueue.new' ;
@@ -2273,6 +2278,14 @@ function commitRootImpl(root, renderPriorityLevel) {
2273
2278
}
2274
2279
}
2275
2280
2281
+ if ( enableDoubleInvokingEffects ) {
2282
+ if ( __DEV__ ) {
2283
+ if ( ! rootDidHavePassiveEffects ) {
2284
+ doubleInvokeEffectsInDEV ( root . current , false ) ;
2285
+ }
2286
+ }
2287
+ }
2288
+
2276
2289
if ( remainingLanes === SyncLane ) {
2277
2290
// Count the number of times the root synchronously re-renders without
2278
2291
// finishing. If there are too many, it indicates an infinite update loop.
@@ -2970,6 +2983,12 @@ function flushPassiveEffectsImpl() {
2970
2983
nestedPassiveUpdateCount =
2971
2984
rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1 ;
2972
2985
2986
+ if ( enableDoubleInvokingEffects ) {
2987
+ if ( __DEV__ ) {
2988
+ doubleInvokeEffectsInDEV ( root . current , true ) ;
2989
+ }
2990
+ }
2991
+
2973
2992
return true ;
2974
2993
}
2975
2994
@@ -3822,6 +3841,195 @@ function finishPendingInteractions(root, committedLanes) {
3822
3841
}
3823
3842
}
3824
3843
3844
+ function doubleInvokeEffectsInDEV ( fiber , hasPassiveEffects ) {
3845
+ if ( __DEV__ ) {
3846
+ invokeLayoutEffectsUnmountInDEV ( fiber ) ;
3847
+ invokeLayoutEffectsMountInDEV ( fiber ) ;
3848
+
3849
+ if ( hasPassiveEffects ) {
3850
+ invokePassiveEffectsUnmountInDEV ( fiber ) ;
3851
+ invokePassiveEffectsMountInDEV ( fiber ) ;
3852
+ }
3853
+ }
3854
+ }
3855
+
3856
+ function invokeLayoutEffectsUnmountInDEV ( firstChild ) {
3857
+ // unmount layout effects
3858
+ let fiber = firstChild ;
3859
+ while ( fiber !== null ) {
3860
+ if ( fiber . child !== null ) {
3861
+ // Should we add a separate subtree tag for this?
3862
+ const primarySubtreeTag = fiber . subtreeTag & LayoutSubtreeTag ;
3863
+ if ( primarySubtreeTag !== NoSubtreeTag ) {
3864
+ invokeLayoutEffectsUnmountInDEV ( fiber . child ) ;
3865
+ }
3866
+ }
3867
+
3868
+ const effectTag = fiber . effectTag ;
3869
+ const current = fiber . alternate ;
3870
+ // This is a mount
3871
+ if ( current === null ) {
3872
+ if ( effectTag & Update ) {
3873
+ switch ( fiber . tag ) {
3874
+ case FunctionComponent :
3875
+ case ForwardRef :
3876
+ case SimpleMemoComponent :
3877
+ case Block : {
3878
+ invokeGuardedCallback (
3879
+ null ,
3880
+ commitHookEffectListUnmount ,
3881
+ null ,
3882
+ HookLayout | HookHasEffect ,
3883
+ fiber ,
3884
+ ) ;
3885
+ if ( hasCaughtError ( ) ) {
3886
+ const unmountError = clearCaughtError ( ) ;
3887
+ captureCommitPhaseError ( fiber , unmountError ) ;
3888
+ }
3889
+ break ;
3890
+ }
3891
+ case ClassComponent : {
3892
+ const instance = fiber . stateNode ;
3893
+ if ( typeof instance . componentWillUnmount === 'function' ) {
3894
+ safelyCallComponentWillUnmount ( fiber , instance ) ;
3895
+ }
3896
+ break ;
3897
+ }
3898
+ }
3899
+ }
3900
+ }
3901
+ fiber = fiber . sibling ;
3902
+ }
3903
+ }
3904
+
3905
+ function invokeLayoutEffectsMountInDEV ( firstChild ) {
3906
+ // mount layout effects
3907
+ if ( __DEV__ ) {
3908
+ let fiber = firstChild ;
3909
+ while ( fiber !== null ) {
3910
+ if ( fiber . child !== null ) {
3911
+ // Should we add a separate subtree tag for this?
3912
+ const primarySubtreeTag = fiber . subtreeTag & LayoutSubtreeTag ;
3913
+ if ( primarySubtreeTag !== NoSubtreeTag ) {
3914
+ invokeLayoutEffectsMountInDEV ( fiber . child ) ;
3915
+ }
3916
+ }
3917
+
3918
+ const effectTag = fiber . effectTag ;
3919
+ const current = fiber . alternate ;
3920
+ if ( current === null ) {
3921
+ if ( effectTag & Update ) {
3922
+ switch ( fiber . tag ) {
3923
+ case FunctionComponent :
3924
+ case ForwardRef :
3925
+ case SimpleMemoComponent :
3926
+ case Block : {
3927
+ invokeGuardedCallback (
3928
+ null ,
3929
+ commitHookEffectListMount ,
3930
+ null ,
3931
+ HookLayout | HookHasEffect ,
3932
+ fiber ,
3933
+ ) ;
3934
+ if ( hasCaughtError ( ) ) {
3935
+ const mountError = clearCaughtError ( ) ;
3936
+ captureCommitPhaseError ( fiber , mountError ) ;
3937
+ }
3938
+ break ;
3939
+ }
3940
+ case ClassComponent : {
3941
+ const instance = fiber . stateNode ;
3942
+ instance . componentDidMount ( ) ;
3943
+ break ;
3944
+ }
3945
+ }
3946
+ }
3947
+ }
3948
+ fiber = fiber . sibling ;
3949
+ }
3950
+ }
3951
+ }
3952
+
3953
+ function invokePassiveEffectsUnmountInDEV ( firstChild ) : void {
3954
+ if ( __DEV__ ) {
3955
+ let fiber = firstChild ;
3956
+ while ( fiber !== null ) {
3957
+ if ( fiber . child !== null ) {
3958
+ const primarySubtreeTag = fiber . subtreeTag & PassiveSubtreeTag ;
3959
+ if ( primarySubtreeTag !== NoSubtreeTag ) {
3960
+ invokePassiveEffectsUnmountInDEV ( fiber . child ) ;
3961
+ }
3962
+ }
3963
+
3964
+ const current = fiber . alternate ;
3965
+ if ( current === null ) {
3966
+ switch ( fiber . tag ) {
3967
+ case FunctionComponent :
3968
+ case ForwardRef :
3969
+ case SimpleMemoComponent :
3970
+ case Block : {
3971
+ if ( fiber . effectTag & Passive ) {
3972
+ invokeGuardedCallback (
3973
+ null ,
3974
+ commitHookEffectListUnmount ,
3975
+ null ,
3976
+ HookPassive | HookHasEffect ,
3977
+ fiber ,
3978
+ ) ;
3979
+ if ( hasCaughtError ( ) ) {
3980
+ const unmountError = clearCaughtError ( ) ;
3981
+ captureCommitPhaseError ( fiber , unmountError ) ;
3982
+ }
3983
+ }
3984
+ break ;
3985
+ }
3986
+ }
3987
+ }
3988
+ fiber = fiber . sibling ;
3989
+ }
3990
+ }
3991
+ }
3992
+
3993
+ function invokePassiveEffectsMountInDEV ( firstChild ) : void {
3994
+ if ( __DEV__ ) {
3995
+ let fiber = firstChild ;
3996
+ while ( fiber !== null ) {
3997
+ if ( fiber . child !== null ) {
3998
+ const primarySubtreeTag = fiber . subtreeTag & PassiveSubtreeTag ;
3999
+ if ( primarySubtreeTag !== NoSubtreeTag ) {
4000
+ invokePassiveEffectsMountInDEV ( fiber . child ) ;
4001
+ }
4002
+ }
4003
+
4004
+ const current = fiber . alternate ;
4005
+ if ( current === null ) {
4006
+ switch ( fiber . tag ) {
4007
+ case FunctionComponent :
4008
+ case ForwardRef :
4009
+ case SimpleMemoComponent :
4010
+ case Block : {
4011
+ if ( fiber . effectTag & Passive ) {
4012
+ invokeGuardedCallback (
4013
+ null ,
4014
+ commitHookEffectListMount ,
4015
+ null ,
4016
+ HookPassive | HookHasEffect ,
4017
+ fiber ,
4018
+ ) ;
4019
+ if ( hasCaughtError ( ) ) {
4020
+ const mountError = clearCaughtError ( ) ;
4021
+ captureCommitPhaseError ( fiber , mountError ) ;
4022
+ }
4023
+ }
4024
+ break ;
4025
+ }
4026
+ }
4027
+ }
4028
+ fiber = fiber . sibling ;
4029
+ }
4030
+ }
4031
+ }
4032
+
3825
4033
// `act` testing API
3826
4034
//
3827
4035
// TODO: This is mostly a copy-paste from the legacy `act`, which does not have
0 commit comments