Put modern StrictMode behind a feature flag (#25365)
* Put modern StrictMode behind a feature flag * Remove unneeded flag
Samuel Susla committed
Sep 30, 2022 at 17:06 UTC
abbbdf4cec992666885cc67344563795a333d4a2
21 files changed
+637
-90
packages/react-reconciler/src/ReactFiberClassComponent.new.js
+45
-6
@@ -13,7 +13,12 @@ import type {UpdateQueue} from './ReactFiberClassUpdateQueue.new';
13
import type {Flags} from './ReactFiberFlags';
14
15
import * as React from 'react';
16
-import {LayoutStatic, Update, Snapshot} from './ReactFiberFlags';
16
+import {
17
+ LayoutStatic,
18
+ Update,
19
+ Snapshot,
20
+ MountLayoutDev,
21
+} from './ReactFiberFlags';
22
import {
23
debugRenderPhaseSideEffectsForStrictMode,
24
disableLegacyContext,
@@ -21,6 +26,7 @@ import {
26
enableSchedulingProfiler,
27
warnAboutDeprecatedLifecycles,
28
enableLazyContextPropagation,
29
+ enableStrictEffects,
30
} from 'shared/ReactFeatureFlags';
31
import ReactStrictModeWarnings from './ReactStrictModeWarnings.new';
32
import {isMounted} from './ReactFiberTreeReflection';
@@ -33,7 +39,12 @@ import isArray from 'shared/isArray';
39
import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
40
41
import {resolveDefaultProps} from './ReactFiberLazyComponent.new';
36
-import {DebugTracingMode, StrictLegacyMode} from './ReactTypeOfMode';
42
+import {
43
+ DebugTracingMode,
44
+ NoMode,
45
+ StrictLegacyMode,
46
+ StrictEffectsMode,
47
+} from './ReactTypeOfMode';
48
49
import {
50
enqueueUpdate,
@@ -896,7 +907,14 @@ function mountClassInstance(
907
}
908
909
if (typeof instance.componentDidMount === 'function') {
899
- const fiberFlags: Flags = Update | LayoutStatic;
910
+ let fiberFlags: Flags = Update | LayoutStatic;
911
+ if (
912
+ __DEV__ &&
913
+ enableStrictEffects &&
914
+ (workInProgress.mode & StrictEffectsMode) !== NoMode
915
+ ) {
916
+ fiberFlags |= MountLayoutDev;
917
+ }
918
workInProgress.flags |= fiberFlags;
919
}
920
}
@@ -967,7 +985,14 @@ function resumeMountClassInstance(
985
// If an update was already in progress, we should schedule an Update
986
// effect even though we're bailing out, so that cWU/cDU are called.
987
if (typeof instance.componentDidMount === 'function') {
970
- const fiberFlags: Flags = Update | LayoutStatic;
988
+ let fiberFlags: Flags = Update | LayoutStatic;
989
+ if (
990
+ __DEV__ &&
991
+ enableStrictEffects &&
992
+ (workInProgress.mode & StrictEffectsMode) !== NoMode
993
+ ) {
994
+ fiberFlags |= MountLayoutDev;
995
+ }
996
workInProgress.flags |= fiberFlags;
997
}
998
return false;
@@ -1011,14 +1036,28 @@ function resumeMountClassInstance(
1036
}
1037
}
1038
if (typeof instance.componentDidMount === 'function') {
1014
- const fiberFlags: Flags = Update | LayoutStatic;
1039
+ let fiberFlags: Flags = Update | LayoutStatic;
1040
+ if (
1041
+ __DEV__ &&
1042
+ enableStrictEffects &&
1043
+ (workInProgress.mode & StrictEffectsMode) !== NoMode
1044
+ ) {
1045
+ fiberFlags |= MountLayoutDev;
1046
+ }
1047
workInProgress.flags |= fiberFlags;
1048
}
1049
} else {
1050
// If an update was already in progress, we should schedule an Update
1051
// effect even though we're bailing out, so that cWU/cDU are called.
1052
if (typeof instance.componentDidMount === 'function') {
1021
- const fiberFlags: Flags = Update | LayoutStatic;
1053
+ let fiberFlags: Flags = Update | LayoutStatic;
1054
+ if (
1055
+ __DEV__ &&
1056
+ enableStrictEffects &&
1057
+ (workInProgress.mode & StrictEffectsMode) !== NoMode
1058
+ ) {
1059
+ fiberFlags |= MountLayoutDev;
1060
+ }
1061
workInProgress.flags |= fiberFlags;
1062
}
1063
packages/react-reconciler/src/ReactFiberClassComponent.old.js
+45
-6
@@ -13,7 +13,12 @@ import type {UpdateQueue} from './ReactFiberClassUpdateQueue.old';
13
import type {Flags} from './ReactFiberFlags';
14
15
import * as React from 'react';
16
-import {LayoutStatic, Update, Snapshot} from './ReactFiberFlags';
16
+import {
17
+ LayoutStatic,
18
+ Update,
19
+ Snapshot,
20
+ MountLayoutDev,
21
+} from './ReactFiberFlags';
22
import {
23
debugRenderPhaseSideEffectsForStrictMode,
24
disableLegacyContext,
@@ -21,6 +26,7 @@ import {
26
enableSchedulingProfiler,
27
warnAboutDeprecatedLifecycles,
28
enableLazyContextPropagation,
29
+ enableStrictEffects,
30
} from 'shared/ReactFeatureFlags';
31
import ReactStrictModeWarnings from './ReactStrictModeWarnings.old';
32
import {isMounted} from './ReactFiberTreeReflection';
@@ -33,7 +39,12 @@ import isArray from 'shared/isArray';
39
import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
40
41
import {resolveDefaultProps} from './ReactFiberLazyComponent.old';
36
-import {DebugTracingMode, StrictLegacyMode} from './ReactTypeOfMode';
42
+import {
43
+ DebugTracingMode,
44
+ NoMode,
45
+ StrictLegacyMode,
46
+ StrictEffectsMode,
47
+} from './ReactTypeOfMode';
48
49
import {
50
enqueueUpdate,
@@ -896,7 +907,14 @@ function mountClassInstance(
907
}
908
909
if (typeof instance.componentDidMount === 'function') {
899
- const fiberFlags: Flags = Update | LayoutStatic;
910
+ let fiberFlags: Flags = Update | LayoutStatic;
911
+ if (
912
+ __DEV__ &&
913
+ enableStrictEffects &&
914
+ (workInProgress.mode & StrictEffectsMode) !== NoMode
915
+ ) {
916
+ fiberFlags |= MountLayoutDev;
917
+ }
918
workInProgress.flags |= fiberFlags;
919
}
920
}
@@ -967,7 +985,14 @@ function resumeMountClassInstance(
985
// If an update was already in progress, we should schedule an Update
986
// effect even though we're bailing out, so that cWU/cDU are called.
987
if (typeof instance.componentDidMount === 'function') {
970
- const fiberFlags: Flags = Update | LayoutStatic;
988
+ let fiberFlags: Flags = Update | LayoutStatic;
989
+ if (
990
+ __DEV__ &&
991
+ enableStrictEffects &&
992
+ (workInProgress.mode & StrictEffectsMode) !== NoMode
993
+ ) {
994
+ fiberFlags |= MountLayoutDev;
995
+ }
996
workInProgress.flags |= fiberFlags;
997
}
998
return false;
@@ -1011,14 +1036,28 @@ function resumeMountClassInstance(
1036
}
1037
}
1038
if (typeof instance.componentDidMount === 'function') {
1014
- const fiberFlags: Flags = Update | LayoutStatic;
1039
+ let fiberFlags: Flags = Update | LayoutStatic;
1040
+ if (
1041
+ __DEV__ &&
1042
+ enableStrictEffects &&
1043
+ (workInProgress.mode & StrictEffectsMode) !== NoMode
1044
+ ) {
1045
+ fiberFlags |= MountLayoutDev;
1046
+ }
1047
workInProgress.flags |= fiberFlags;
1048
}
1049
} else {
1050
// If an update was already in progress, we should schedule an Update
1051
// effect even though we're bailing out, so that cWU/cDU are called.
1052
if (typeof instance.componentDidMount === 'function') {
1021
- const fiberFlags: Flags = Update | LayoutStatic;
1053
+ let fiberFlags: Flags = Update | LayoutStatic;
1054
+ if (
1055
+ __DEV__ &&
1056
+ enableStrictEffects &&
1057
+ (workInProgress.mode & StrictEffectsMode) !== NoMode
1058
+ ) {
1059
+ fiberFlags |= MountLayoutDev;
1060
+ }
1061
workInProgress.flags |= fiberFlags;
1062
}
1063
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+109
-1
@@ -50,6 +50,7 @@ import {
50
enableCache,
51
enableTransitionTracing,
52
enableUseEventHook,
53
+ enableStrictEffects,
54
} from 'shared/ReactFeatureFlags';
55
import {
56
FunctionComponent,
@@ -4109,4 +4110,111 @@ function commitPassiveUnmountInsideDeletedTreeOnFiber(
4110
}
4111
}
4112
4112
-export {commitPlacement, commitAttachRef, commitDetachRef};
4113
+function invokeLayoutEffectMountInDEV(fiber: Fiber): void {
4114
+ if (__DEV__ && enableStrictEffects) {
4115
+ // We don't need to re-check StrictEffectsMode here.
4116
+ // This function is only called if that check has already passed.
4117
+ switch (fiber.tag) {
4118
+ case FunctionComponent:
4119
+ case ForwardRef:
4120
+ case SimpleMemoComponent: {
4121
+ try {
4122
+ commitHookEffectListMount(HookLayout | HookHasEffect, fiber);
4123
+ } catch (error) {
4124
+ captureCommitPhaseError(fiber, fiber.return, error);
4125
+ }
4126
+ break;
4127
+ }
4128
+ case ClassComponent: {
4129
+ const instance = fiber.stateNode;
4130
+ try {
4131
+ instance.componentDidMount();
4132
+ } catch (error) {
4133
+ captureCommitPhaseError(fiber, fiber.return, error);
4134
+ }
4135
+ break;
4136
+ }
4137
+ }
4138
+ }
4139
+}
4140
+
4141
+function invokePassiveEffectMountInDEV(fiber: Fiber): void {
4142
+ if (__DEV__ && enableStrictEffects) {
4143
+ // We don't need to re-check StrictEffectsMode here.
4144
+ // This function is only called if that check has already passed.
4145
+ switch (fiber.tag) {
4146
+ case FunctionComponent:
4147
+ case ForwardRef:
4148
+ case SimpleMemoComponent: {
4149
+ try {
4150
+ commitHookEffectListMount(HookPassive | HookHasEffect, fiber);
4151
+ } catch (error) {
4152
+ captureCommitPhaseError(fiber, fiber.return, error);
4153
+ }
4154
+ break;
4155
+ }
4156
+ }
4157
+ }
4158
+}
4159
+
4160
+function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {
4161
+ if (__DEV__ && enableStrictEffects) {
4162
+ // We don't need to re-check StrictEffectsMode here.
4163
+ // This function is only called if that check has already passed.
4164
+ switch (fiber.tag) {
4165
+ case FunctionComponent:
4166
+ case ForwardRef:
4167
+ case SimpleMemoComponent: {
4168
+ try {
4169
+ commitHookEffectListUnmount(
4170
+ HookLayout | HookHasEffect,
4171
+ fiber,
4172
+ fiber.return,
4173
+ );
4174
+ } catch (error) {
4175
+ captureCommitPhaseError(fiber, fiber.return, error);
4176
+ }
4177
+ break;
4178
+ }
4179
+ case ClassComponent: {
4180
+ const instance = fiber.stateNode;
4181
+ if (typeof instance.componentWillUnmount === 'function') {
4182
+ safelyCallComponentWillUnmount(fiber, fiber.return, instance);
4183
+ }
4184
+ break;
4185
+ }
4186
+ }
4187
+ }
4188
+}
4189
+
4190
+function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {
4191
+ if (__DEV__ && enableStrictEffects) {
4192
+ // We don't need to re-check StrictEffectsMode here.
4193
+ // This function is only called if that check has already passed.
4194
+ switch (fiber.tag) {
4195
+ case FunctionComponent:
4196
+ case ForwardRef:
4197
+ case SimpleMemoComponent: {
4198
+ try {
4199
+ commitHookEffectListUnmount(
4200
+ HookPassive | HookHasEffect,
4201
+ fiber,
4202
+ fiber.return,
4203
+ );
4204
+ } catch (error) {
4205
+ captureCommitPhaseError(fiber, fiber.return, error);
4206
+ }
4207
+ }
4208
+ }
4209
+ }
4210
+}
4211
+
4212
+export {
4213
+ commitPlacement,
4214
+ commitAttachRef,
4215
+ commitDetachRef,
4216
+ invokeLayoutEffectMountInDEV,
4217
+ invokeLayoutEffectUnmountInDEV,
4218
+ invokePassiveEffectMountInDEV,
4219
+ invokePassiveEffectUnmountInDEV,
4220
+};
packages/react-reconciler/src/ReactFiberCommitWork.old.js
+109
-1
@@ -50,6 +50,7 @@ import {
50
enableCache,
51
enableTransitionTracing,
52
enableUseEventHook,
53
+ enableStrictEffects,
54
} from 'shared/ReactFeatureFlags';
55
import {
56
FunctionComponent,
@@ -4109,4 +4110,111 @@ function commitPassiveUnmountInsideDeletedTreeOnFiber(
4110
}
4111
}
4112
4112
-export {commitPlacement, commitAttachRef, commitDetachRef};
4113
+function invokeLayoutEffectMountInDEV(fiber: Fiber): void {
4114
+ if (__DEV__ && enableStrictEffects) {
4115
+ // We don't need to re-check StrictEffectsMode here.
4116
+ // This function is only called if that check has already passed.
4117
+ switch (fiber.tag) {
4118
+ case FunctionComponent:
4119
+ case ForwardRef:
4120
+ case SimpleMemoComponent: {
4121
+ try {
4122
+ commitHookEffectListMount(HookLayout | HookHasEffect, fiber);
4123
+ } catch (error) {
4124
+ captureCommitPhaseError(fiber, fiber.return, error);
4125
+ }
4126
+ break;
4127
+ }
4128
+ case ClassComponent: {
4129
+ const instance = fiber.stateNode;
4130
+ try {
4131
+ instance.componentDidMount();
4132
+ } catch (error) {
4133
+ captureCommitPhaseError(fiber, fiber.return, error);
4134
+ }
4135
+ break;
4136
+ }
4137
+ }
4138
+ }
4139
+}
4140
+
4141
+function invokePassiveEffectMountInDEV(fiber: Fiber): void {
4142
+ if (__DEV__ && enableStrictEffects) {
4143
+ // We don't need to re-check StrictEffectsMode here.
4144
+ // This function is only called if that check has already passed.
4145
+ switch (fiber.tag) {
4146
+ case FunctionComponent:
4147
+ case ForwardRef:
4148
+ case SimpleMemoComponent: {
4149
+ try {
4150
+ commitHookEffectListMount(HookPassive | HookHasEffect, fiber);
4151
+ } catch (error) {
4152
+ captureCommitPhaseError(fiber, fiber.return, error);
4153
+ }
4154
+ break;
4155
+ }
4156
+ }
4157
+ }
4158
+}
4159
+
4160
+function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {
4161
+ if (__DEV__ && enableStrictEffects) {
4162
+ // We don't need to re-check StrictEffectsMode here.
4163
+ // This function is only called if that check has already passed.
4164
+ switch (fiber.tag) {
4165
+ case FunctionComponent:
4166
+ case ForwardRef:
4167
+ case SimpleMemoComponent: {
4168
+ try {
4169
+ commitHookEffectListUnmount(
4170
+ HookLayout | HookHasEffect,
4171
+ fiber,
4172
+ fiber.return,
4173
+ );
4174
+ } catch (error) {
4175
+ captureCommitPhaseError(fiber, fiber.return, error);
4176
+ }
4177
+ break;
4178
+ }
4179
+ case ClassComponent: {
4180
+ const instance = fiber.stateNode;
4181
+ if (typeof instance.componentWillUnmount === 'function') {
4182
+ safelyCallComponentWillUnmount(fiber, fiber.return, instance);
4183
+ }
4184
+ break;
4185
+ }
4186
+ }
4187
+ }
4188
+}
4189
+
4190
+function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {
4191
+ if (__DEV__ && enableStrictEffects) {
4192
+ // We don't need to re-check StrictEffectsMode here.
4193
+ // This function is only called if that check has already passed.
4194
+ switch (fiber.tag) {
4195
+ case FunctionComponent:
4196
+ case ForwardRef:
4197
+ case SimpleMemoComponent: {
4198
+ try {
4199
+ commitHookEffectListUnmount(
4200
+ HookPassive | HookHasEffect,
4201
+ fiber,
4202
+ fiber.return,
4203
+ );
4204
+ } catch (error) {
4205
+ captureCommitPhaseError(fiber, fiber.return, error);
4206
+ }
4207
+ }
4208
+ }
4209
+ }
4210
+}
4211
+
4212
+export {
4213
+ commitPlacement,
4214
+ commitAttachRef,
4215
+ commitDetachRef,
4216
+ invokeLayoutEffectMountInDEV,
4217
+ invokeLayoutEffectUnmountInDEV,
4218
+ invokePassiveEffectMountInDEV,
4219
+ invokePassiveEffectUnmountInDEV,
4220
+};
packages/react-reconciler/src/ReactFiberFlags.js
+28
-26
@@ -12,49 +12,51 @@ import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
12
export type Flags = number;
13
14
// Don't change these two values. They're used by React Dev Tools.
15
-export const NoFlags = /* */ 0b000000000000000000000000;
16
-export const PerformedWork = /* */ 0b000000000000000000000001;
15
+export const NoFlags = /* */ 0b00000000000000000000000000;
16
+export const PerformedWork = /* */ 0b00000000000000000000000001;
17
18
// You can change the rest (and add more).
19
-export const Placement = /* */ 0b000000000000000000000010;
20
-export const Update = /* */ 0b000000000000000000000100;
21
-export const ChildDeletion = /* */ 0b000000000000000000001000;
22
-export const ContentReset = /* */ 0b000000000000000000010000;
23
-export const Callback = /* */ 0b000000000000000000100000;
24
-export const DidCapture = /* */ 0b000000000000000001000000;
25
-export const ForceClientRender = /* */ 0b000000000000000010000000;
26
-export const Ref = /* */ 0b000000000000000100000000;
27
-export const Snapshot = /* */ 0b000000000000001000000000;
28
-export const Passive = /* */ 0b000000000000010000000000;
29
-export const Hydrating = /* */ 0b000000000000100000000000;
30
-export const Visibility = /* */ 0b000000000001000000000000;
31
-export const StoreConsistency = /* */ 0b000000000010000000000000;
19
+export const Placement = /* */ 0b00000000000000000000000010;
20
+export const Update = /* */ 0b00000000000000000000000100;
21
+export const ChildDeletion = /* */ 0b00000000000000000000001000;
22
+export const ContentReset = /* */ 0b00000000000000000000010000;
23
+export const Callback = /* */ 0b00000000000000000000100000;
24
+export const DidCapture = /* */ 0b00000000000000000001000000;
25
+export const ForceClientRender = /* */ 0b00000000000000000010000000;
26
+export const Ref = /* */ 0b00000000000000000100000000;
27
+export const Snapshot = /* */ 0b00000000000000001000000000;
28
+export const Passive = /* */ 0b00000000000000010000000000;
29
+export const Hydrating = /* */ 0b00000000000000100000000000;
30
+export const Visibility = /* */ 0b00000000000001000000000000;
31
+export const StoreConsistency = /* */ 0b00000000000010000000000000;
32
33
export const LifecycleEffectMask =
34
Passive | Update | Callback | Ref | Snapshot | StoreConsistency;
35
36
// Union of all commit flags (flags with the lifetime of a particular commit)
37
-export const HostEffectMask = /* */ 0b000000000011111111111111;
37
+export const HostEffectMask = /* */ 0b00000000000011111111111111;
38
39
// These are not really side effects, but we still reuse this field.
40
-export const Incomplete = /* */ 0b000000000100000000000000;
41
-export const ShouldCapture = /* */ 0b000000001000000000000000;
42
-export const ForceUpdateForLegacySuspense = /* */ 0b000000010000000000000000;
43
-export const DidPropagateContext = /* */ 0b000000100000000000000000;
44
-export const NeedsPropagation = /* */ 0b000001000000000000000000;
45
-export const Forked = /* */ 0b000010000000000000000000;
40
+export const Incomplete = /* */ 0b00000000000100000000000000;
41
+export const ShouldCapture = /* */ 0b00000000001000000000000000;
42
+export const ForceUpdateForLegacySuspense = /* */ 0b00000000010000000000000000;
43
+export const DidPropagateContext = /* */ 0b00000000100000000000000000;
44
+export const NeedsPropagation = /* */ 0b00000001000000000000000000;
45
+export const Forked = /* */ 0b00000010000000000000000000;
46
47
// Static tags describe aspects of a fiber that are not specific to a render,
48
// e.g. a fiber uses a passive effect (even if there are no updates on this particular render).
49
// This enables us to defer more work in the unmount case,
50
// since we can defer traversing the tree during layout to look for Passive effects,
51
// and instead rely on the static flag as a signal that there may be cleanup work.
52
-export const RefStatic = /* */ 0b000100000000000000000000;
53
-export const LayoutStatic = /* */ 0b001000000000000000000000;
54
-export const PassiveStatic = /* */ 0b010000000000000000000000;
52
+export const RefStatic = /* */ 0b00000100000000000000000000;
53
+export const LayoutStatic = /* */ 0b00001000000000000000000000;
54
+export const PassiveStatic = /* */ 0b00010000000000000000000000;
55
56
// Flag used to identify newly inserted fibers. It isn't reset after commit unlike `Placement`.
57
-export const PlacementDEV = /* */ 0b100000000000000000000000;
57
+export const PlacementDEV = /* */ 0b00100000000000000000000000;
58
+export const MountLayoutDev = /* */ 0b01000000000000000000000000;
59
+export const MountPassiveDev = /* */ 0b10000000000000000000000000;
60
61
// Groups of flags that are used in the commit phase to skip over trees that
62
// don't contain effects, by checking subtreeFlags.
packages/react-reconciler/src/ReactFiberHooks.new.js
+60
-10
@@ -42,13 +42,19 @@ import {
42
enableUseHook,
43
enableUseMemoCacheHook,
44
enableUseEventHook,
45
+ enableStrictEffects,
46
} from 'shared/ReactFeatureFlags';
47
import {
48
REACT_CONTEXT_TYPE,
49
REACT_SERVER_CONTEXT_TYPE,
50
} from 'shared/ReactSymbols';
51
51
-import {NoMode, ConcurrentMode, DebugTracingMode} from './ReactTypeOfMode';
52
+import {
53
+ NoMode,
54
+ ConcurrentMode,
55
+ DebugTracingMode,
56
+ StrictEffectsMode,
57
+} from './ReactTypeOfMode';
58
import {
59
NoLane,
60
SyncLane,
@@ -81,6 +87,8 @@ import {
87
StaticMask as StaticMaskEffect,
88
Update as UpdateEffect,
89
StoreConsistency,
90
+ MountLayoutDev as MountLayoutDevEffect,
91
+ MountPassiveDev as MountPassiveDevEffect,
92
} from './ReactFiberFlags';
93
import {
94
HasEffect as HookHasEffect,
@@ -590,7 +598,22 @@ export function bailoutHooks(
598
lanes: Lanes,
599
) {
600
workInProgress.updateQueue = current.updateQueue;
593
- workInProgress.flags &= ~(PassiveEffect | UpdateEffect);
601
+ // TODO: Don't need to reset the flags here, because they're reset in the
602
+ // complete phase (bubbleProperties).
603
+ if (
604
+ __DEV__ &&
605
+ enableStrictEffects &&
606
+ (workInProgress.mode & StrictEffectsMode) !== NoMode
607
+ ) {
608
+ workInProgress.flags &= ~(
609
+ MountPassiveDevEffect |
610
+ MountLayoutDevEffect |
611
+ PassiveEffect |
612
+ UpdateEffect
613
+ );
614
+ } else {
615
+ workInProgress.flags &= ~(PassiveEffect | UpdateEffect);
616
+ }
617
current.lanes = removeLanes(current.lanes, lanes);
618
}
619
@@ -1859,12 +1882,25 @@ function mountEffect(
1882
create: () => (() => void) | void,
1883
deps: Array<mixed> | void | null,
1884
): void {
1862
- return mountEffectImpl(
1863
- PassiveEffect | PassiveStaticEffect,
1864
- HookPassive,
1865
- create,
1866
- deps,
1867
- );
1885
+ if (
1886
+ __DEV__ &&
1887
+ enableStrictEffects &&
1888
+ (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode
1889
+ ) {
1890
+ return mountEffectImpl(
1891
+ MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect,
1892
+ HookPassive,
1893
+ create,
1894
+ deps,
1895
+ );
1896
+ } else {
1897
+ return mountEffectImpl(
1898
+ PassiveEffect | PassiveStaticEffect,
1899
+ HookPassive,
1900
+ create,
1901
+ deps,
1902
+ );
1903
+ }
1904
}
1905
1906
function updateEffect(
@@ -1940,7 +1976,14 @@ function mountLayoutEffect(
1976
create: () => (() => void) | void,
1977
deps: Array<mixed> | void | null,
1978
): void {
1943
- const fiberFlags: Flags = UpdateEffect | LayoutStaticEffect;
1979
+ let fiberFlags: Flags = UpdateEffect | LayoutStaticEffect;
1980
+ if (
1981
+ __DEV__ &&
1982
+ enableStrictEffects &&
1983
+ (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode
1984
+ ) {
1985
+ fiberFlags |= MountLayoutDevEffect;
1986
+ }
1987
return mountEffectImpl(fiberFlags, HookLayout, create, deps);
1988
}
1989
@@ -2000,7 +2043,14 @@ function mountImperativeHandle<T>(
2043
const effectDeps =
2044
deps !== null && deps !== undefined ? deps.concat([ref]) : null;
2045
2003
- const fiberFlags: Flags = UpdateEffect | LayoutStaticEffect;
2046
+ let fiberFlags: Flags = UpdateEffect | LayoutStaticEffect;
2047
+ if (
2048
+ __DEV__ &&
2049
+ enableStrictEffects &&
2050
+ (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode
2051
+ ) {
2052
+ fiberFlags |= MountLayoutDevEffect;
2053
+ }
2054
return mountEffectImpl(
2055
fiberFlags,
2056
HookLayout,
packages/react-reconciler/src/ReactFiberHooks.old.js
+60
-10
@@ -42,13 +42,19 @@ import {
42
enableUseHook,
43
enableUseMemoCacheHook,
44
enableUseEventHook,
45
+ enableStrictEffects,
46
} from 'shared/ReactFeatureFlags';
47
import {
48
REACT_CONTEXT_TYPE,
49
REACT_SERVER_CONTEXT_TYPE,
50
} from 'shared/ReactSymbols';
51
51
-import {NoMode, ConcurrentMode, DebugTracingMode} from './ReactTypeOfMode';
52
+import {
53
+ NoMode,
54
+ ConcurrentMode,
55
+ DebugTracingMode,
56
+ StrictEffectsMode,
57
+} from './ReactTypeOfMode';
58
import {
59
NoLane,
60
SyncLane,
@@ -81,6 +87,8 @@ import {
87
StaticMask as StaticMaskEffect,
88
Update as UpdateEffect,
89
StoreConsistency,
90
+ MountLayoutDev as MountLayoutDevEffect,
91
+ MountPassiveDev as MountPassiveDevEffect,
92
} from './ReactFiberFlags';
93
import {
94
HasEffect as HookHasEffect,
@@ -590,7 +598,22 @@ export function bailoutHooks(
598
lanes: Lanes,
599
) {
600
workInProgress.updateQueue = current.updateQueue;
593
- workInProgress.flags &= ~(PassiveEffect | UpdateEffect);
601
+ // TODO: Don't need to reset the flags here, because they're reset in the
602
+ // complete phase (bubbleProperties).
603
+ if (
604
+ __DEV__ &&
605
+ enableStrictEffects &&
606
+ (workInProgress.mode & StrictEffectsMode) !== NoMode
607
+ ) {
608
+ workInProgress.flags &= ~(
609
+ MountPassiveDevEffect |
610
+ MountLayoutDevEffect |
611
+ PassiveEffect |
612
+ UpdateEffect
613
+ );
614
+ } else {
615
+ workInProgress.flags &= ~(PassiveEffect | UpdateEffect);
616
+ }
617
current.lanes = removeLanes(current.lanes, lanes);
618
}
619
@@ -1859,12 +1882,25 @@ function mountEffect(
1882
create: () => (() => void) | void,
1883
deps: Array<mixed> | void | null,
1884
): void {
1862
- return mountEffectImpl(
1863
- PassiveEffect | PassiveStaticEffect,
1864
- HookPassive,
1865
- create,
1866
- deps,
1867
- );
1885
+ if (
1886
+ __DEV__ &&
1887
+ enableStrictEffects &&
1888
+ (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode
1889
+ ) {
1890
+ return mountEffectImpl(
1891
+ MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect,
1892
+ HookPassive,
1893
+ create,
1894
+ deps,
1895
+ );
1896
+ } else {
1897
+ return mountEffectImpl(
1898
+ PassiveEffect | PassiveStaticEffect,
1899
+ HookPassive,
1900
+ create,
1901
+ deps,
1902
+ );
1903
+ }
1904
}
1905
1906
function updateEffect(
@@ -1940,7 +1976,14 @@ function mountLayoutEffect(
1976
create: () => (() => void) | void,
1977
deps: Array<mixed> | void | null,
1978
): void {
1943
- const fiberFlags: Flags = UpdateEffect | LayoutStaticEffect;
1979
+ let fiberFlags: Flags = UpdateEffect | LayoutStaticEffect;
1980
+ if (
1981
+ __DEV__ &&
1982
+ enableStrictEffects &&
1983
+ (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode
1984
+ ) {
1985
+ fiberFlags |= MountLayoutDevEffect;
1986
+ }
1987
return mountEffectImpl(fiberFlags, HookLayout, create, deps);
1988
}
1989
@@ -2000,7 +2043,14 @@ function mountImperativeHandle<T>(
2043
const effectDeps =
2044
deps !== null && deps !== undefined ? deps.concat([ref]) : null;
2045
2003
- const fiberFlags: Flags = UpdateEffect | LayoutStaticEffect;
2046
+ let fiberFlags: Flags = UpdateEffect | LayoutStaticEffect;
2047
+ if (
2048
+ __DEV__ &&
2049
+ enableStrictEffects &&
2050
+ (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode
2051
+ ) {
2052
+ fiberFlags |= MountLayoutDevEffect;
2053
+ }
2054
return mountEffectImpl(
2055
fiberFlags,
2056
HookLayout,
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+79
-14
@@ -40,6 +40,7 @@ import {
40
enableUpdaterTracking,
41
enableCache,
42
enableTransitionTracing,
43
+ useModernStrictMode,
44
} from 'shared/ReactFeatureFlags';
45
import ReactSharedInternals from 'shared/ReactSharedInternals';
46
import is from 'shared/objectIs';
@@ -113,6 +114,7 @@ import {
114
Profiler,
115
} from './ReactWorkTags';
116
import {ConcurrentRoot, LegacyRoot} from './ReactRootTags';
117
+import type {Flags} from './ReactFiberFlags';
118
import {
119
NoFlags,
120
Incomplete,
@@ -125,6 +127,8 @@ import {
127
PassiveMask,
128
PlacementDEV,
129
Visibility,
130
+ MountPassiveDev,
131
+ MountLayoutDev,
132
} from './ReactFiberFlags';
133
import {
134
NoLanes,
@@ -189,6 +193,10 @@ import {
193
reappearLayoutEffects,
194
disconnectPassiveEffect,
195
reportUncaughtErrorInDEV,
196
+ invokeLayoutEffectMountInDEV,
197
+ invokePassiveEffectMountInDEV,
198
+ invokeLayoutEffectUnmountInDEV,
199
+ invokePassiveEffectUnmountInDEV,
200
} from './ReactFiberCommitWork.new';
201
import {enqueueUpdate} from './ReactFiberClassUpdateQueue.new';
202
import {resetContextDependencies} from './ReactFiberNewContext.new';
@@ -2570,7 +2578,7 @@ function commitRootImpl(
2578
2579
if (__DEV__ && enableStrictEffects) {
2580
if (!rootDidHavePassiveEffects) {
2573
- commitDoubleInvokeEffectsInDEV(root);
2581
+ commitDoubleInvokeEffectsInDEV(root, false);
2582
}
2583
}
2584
@@ -2846,7 +2854,7 @@ function flushPassiveEffectsImpl() {
2854
}
2855
2856
if (__DEV__ && enableStrictEffects) {
2849
- commitDoubleInvokeEffectsInDEV(root);
2857
+ commitDoubleInvokeEffectsInDEV(root, true);
2858
}
2859
2860
executionContext = prevExecutionContext;
@@ -3309,24 +3317,81 @@ function doubleInvokeEffectsInDEVIfNecessary(
3317
}
3318
}
3319
3312
-function commitDoubleInvokeEffectsInDEV(root: FiberRoot) {
3320
+function commitDoubleInvokeEffectsInDEV(
3321
+ root: FiberRoot,
3322
+ hasPassiveEffects: boolean,
3323
+) {
3324
if (__DEV__ && enableStrictEffects) {
3314
- let doubleInvokeEffects = true;
3325
+ if (useModernStrictMode) {
3326
+ let doubleInvokeEffects = true;
3327
3316
- if (root.tag === LegacyRoot && !(root.current.mode & StrictLegacyMode)) {
3317
- doubleInvokeEffects = false;
3328
+ if (root.tag === LegacyRoot && !(root.current.mode & StrictLegacyMode)) {
3329
+ doubleInvokeEffects = false;
3330
+ }
3331
+ if (
3332
+ root.tag === ConcurrentRoot &&
3333
+ !(root.current.mode & (StrictLegacyMode | StrictEffectsMode))
3334
+ ) {
3335
+ doubleInvokeEffects = false;
3336
+ }
3337
+ recursivelyTraverseAndDoubleInvokeEffectsInDEV(
3338
+ root,
3339
+ root.current,
3340
+ doubleInvokeEffects,
3341
+ );
3342
+ } else {
3343
+ legacyCommitDoubleInvokeEffectsInDEV(root.current, hasPassiveEffects);
3344
}
3345
+ }
3346
+}
3347
+
3348
+function legacyCommitDoubleInvokeEffectsInDEV(
3349
+ fiber: Fiber,
3350
+ hasPassiveEffects: boolean,
3351
+) {
3352
+ // TODO (StrictEffects) Should we set a marker on the root if it contains strict effects
3353
+ // so we don't traverse unnecessarily? similar to subtreeFlags but just at the root level.
3354
+ // Maybe not a big deal since this is DEV only behavior.
3355
+
3356
+ setCurrentDebugFiberInDEV(fiber);
3357
+ invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV);
3358
+ if (hasPassiveEffects) {
3359
+ invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectUnmountInDEV);
3360
+ }
3361
+
3362
+ invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV);
3363
+ if (hasPassiveEffects) {
3364
+ invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV);
3365
+ }
3366
+ resetCurrentDebugFiberInDEV();
3367
+}
3368
+
3369
+function invokeEffectsInDev(
3370
+ firstChild: Fiber,
3371
+ fiberFlags: Flags,
3372
+ invokeEffectFn: (fiber: Fiber) => void,
3373
+) {
3374
+ let current = firstChild;
3375
+ let subtreeRoot = null;
3376
+ while (current != null) {
3377
+ const primarySubtreeFlag = current.subtreeFlags & fiberFlags;
3378
if (
3320
- root.tag === ConcurrentRoot &&
3321
- !(root.current.mode & (StrictLegacyMode | StrictEffectsMode))
3379
+ current !== subtreeRoot &&
3380
+ current.child != null &&
3381
+ primarySubtreeFlag !== NoFlags
3382
) {
3323
- doubleInvokeEffects = false;
3383
+ current = current.child;
3384
+ } else {
3385
+ if ((current.flags & fiberFlags) !== NoFlags) {
3386
+ invokeEffectFn(current);
3387
+ }
3388
+
3389
+ if (current.sibling !== null) {
3390
+ current = current.sibling;
3391
+ } else {
3392
+ current = subtreeRoot = current.return;
3393
+ }
3394
}
3325
- recursivelyTraverseAndDoubleInvokeEffectsInDEV(
3326
- root,
3327
- root.current,
3328
- doubleInvokeEffects,
3329
- );
3395
}
3396
}
3397
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+79
-14
@@ -40,6 +40,7 @@ import {
40
enableUpdaterTracking,
41
enableCache,
42
enableTransitionTracing,
43
+ useModernStrictMode,
44
} from 'shared/ReactFeatureFlags';
45
import ReactSharedInternals from 'shared/ReactSharedInternals';
46
import is from 'shared/objectIs';
@@ -113,6 +114,7 @@ import {
114
Profiler,
115
} from './ReactWorkTags';
116
import {ConcurrentRoot, LegacyRoot} from './ReactRootTags';
117
+import type {Flags} from './ReactFiberFlags';
118
import {
119
NoFlags,
120
Incomplete,
@@ -125,6 +127,8 @@ import {
127
PassiveMask,
128
PlacementDEV,
129
Visibility,
130
+ MountPassiveDev,
131
+ MountLayoutDev,
132
} from './ReactFiberFlags';
133
import {
134
NoLanes,
@@ -189,6 +193,10 @@ import {
193
reappearLayoutEffects,
194
disconnectPassiveEffect,
195
reportUncaughtErrorInDEV,
196
+ invokeLayoutEffectMountInDEV,
197
+ invokePassiveEffectMountInDEV,
198
+ invokeLayoutEffectUnmountInDEV,
199
+ invokePassiveEffectUnmountInDEV,
200
} from './ReactFiberCommitWork.old';
201
import {enqueueUpdate} from './ReactFiberClassUpdateQueue.old';
202
import {resetContextDependencies} from './ReactFiberNewContext.old';
@@ -2570,7 +2578,7 @@ function commitRootImpl(
2578
2579
if (__DEV__ && enableStrictEffects) {
2580
if (!rootDidHavePassiveEffects) {
2573
- commitDoubleInvokeEffectsInDEV(root);
2581
+ commitDoubleInvokeEffectsInDEV(root, false);
2582
}
2583
}
2584
@@ -2846,7 +2854,7 @@ function flushPassiveEffectsImpl() {
2854
}
2855
2856
if (__DEV__ && enableStrictEffects) {
2849
- commitDoubleInvokeEffectsInDEV(root);
2857
+ commitDoubleInvokeEffectsInDEV(root, true);
2858
}
2859
2860
executionContext = prevExecutionContext;
@@ -3309,24 +3317,81 @@ function doubleInvokeEffectsInDEVIfNecessary(
3317
}
3318
}
3319
3312
-function commitDoubleInvokeEffectsInDEV(root: FiberRoot) {
3320
+function commitDoubleInvokeEffectsInDEV(
3321
+ root: FiberRoot,
3322
+ hasPassiveEffects: boolean,
3323
+) {
3324
if (__DEV__ && enableStrictEffects) {
3314
- let doubleInvokeEffects = true;
3325
+ if (useModernStrictMode) {
3326
+ let doubleInvokeEffects = true;
3327
3316
- if (root.tag === LegacyRoot && !(root.current.mode & StrictLegacyMode)) {
3317
- doubleInvokeEffects = false;
3328
+ if (root.tag === LegacyRoot && !(root.current.mode & StrictLegacyMode)) {
3329
+ doubleInvokeEffects = false;
3330
+ }
3331
+ if (
3332
+ root.tag === ConcurrentRoot &&
3333
+ !(root.current.mode & (StrictLegacyMode | StrictEffectsMode))
3334
+ ) {
3335
+ doubleInvokeEffects = false;
3336
+ }
3337
+ recursivelyTraverseAndDoubleInvokeEffectsInDEV(
3338
+ root,
3339
+ root.current,
3340
+ doubleInvokeEffects,
3341
+ );
3342
+ } else {
3343
+ legacyCommitDoubleInvokeEffectsInDEV(root.current, hasPassiveEffects);
3344
}
3345
+ }
3346
+}
3347
+
3348
+function legacyCommitDoubleInvokeEffectsInDEV(
3349
+ fiber: Fiber,
3350
+ hasPassiveEffects: boolean,
3351
+) {
3352
+ // TODO (StrictEffects) Should we set a marker on the root if it contains strict effects
3353
+ // so we don't traverse unnecessarily? similar to subtreeFlags but just at the root level.
3354
+ // Maybe not a big deal since this is DEV only behavior.
3355
+
3356
+ setCurrentDebugFiberInDEV(fiber);
3357
+ invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV);
3358
+ if (hasPassiveEffects) {
3359
+ invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectUnmountInDEV);
3360
+ }
3361
+
3362
+ invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV);
3363
+ if (hasPassiveEffects) {
3364
+ invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV);
3365
+ }
3366
+ resetCurrentDebugFiberInDEV();
3367
+}
3368
+
3369
+function invokeEffectsInDev(
3370
+ firstChild: Fiber,
3371
+ fiberFlags: Flags,
3372
+ invokeEffectFn: (fiber: Fiber) => void,
3373
+) {
3374
+ let current = firstChild;
3375
+ let subtreeRoot = null;
3376
+ while (current != null) {
3377
+ const primarySubtreeFlag = current.subtreeFlags & fiberFlags;
3378
if (
3320
- root.tag === ConcurrentRoot &&
3321
- !(root.current.mode & (StrictLegacyMode | StrictEffectsMode))
3379
+ current !== subtreeRoot &&
3380
+ current.child != null &&
3381
+ primarySubtreeFlag !== NoFlags
3382
) {
3323
- doubleInvokeEffects = false;
3383
+ current = current.child;
3384
+ } else {
3385
+ if ((current.flags & fiberFlags) !== NoFlags) {
3386
+ invokeEffectFn(current);
3387
+ }
3388
+
3389
+ if (current.sibling !== null) {
3390
+ current = current.sibling;
3391
+ } else {
3392
+ current = subtreeRoot = current.return;
3393
+ }
3394
}
3325
- recursivelyTraverseAndDoubleInvokeEffectsInDEV(
3326
- root,
3327
- root.current,
3328
- doubleInvokeEffects,
3329
- );
3395
}
3396
}
3397
packages/react-reconciler/src/__tests__/ReactOffscreenStrictMode-test.js
+1
-1
@@ -55,7 +55,7 @@ describe('ReactOffscreenStrictMode', () => {
55
]);
56
});
57
58
- // @gate __DEV__ && enableStrictEffects && enableOffscreen
58
+ // @gate __DEV__ && enableStrictEffects && enableOffscreen && useModernStrictMode
59
it('should not trigger strict effects when offscreen is hidden', () => {
60
act(() => {
61
ReactNoop.render(
packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js
+1
@@ -379,6 +379,7 @@ describe('StrictEffectsMode defaults', () => {
379
expect(Scheduler).toHaveYielded([]);
380
});
381
382
+ //@gate useModernStrictMode
383
it('disconnects refs during double invoking', () => {
384
const onRefMock = jest.fn();
385
function App({text}) {
packages/react/src/__tests__/ReactStrictMode-test.js
+1
-1
@@ -67,7 +67,7 @@ describe('ReactStrictMode', () => {
67
);
68
});
69
70
- // @gate __DEV__ && !enableStrictEffects
70
+ // @gate __DEV__
71
it('should invoke precommit lifecycle methods twice', () => {
72
let log = [];
73
let shouldComponentUpdate = false;
packages/shared/ReactFeatureFlags.js
+5
@@ -260,3 +260,8 @@ export const enableGetInspectorDataForInstanceInProduction = false;
260
export const enableProfilerNestedUpdateScheduledHook = false;
261
262
export const consoleManagedByDevToolsDuringStrictMode = true;
263
+
264
+// Modern <StrictMode /> behaviour aligns more with what components
265
+// components will encounter in production, especially when used With <Offscreen />.
266
+// TODO: clean up legacy <StrictMode /> once tests pass WWW.
267
+export const useModernStrictMode = false;
packages/shared/forks/ReactFeatureFlags.native-fb.js
+2
@@ -84,6 +84,8 @@ export const enableUseMutableSource = true;
84
export const enableTransitionTracing = false;
85
86
export const enableFloat = false;
87
+
88
+export const useModernStrictMode = false;
89
// Flow magic to verify the exports of this file match the original version.
90
// eslint-disable-next-line no-unused-vars
91
type Check<_X, Y: _X, X: Y = _X> = null;
packages/shared/forks/ReactFeatureFlags.native-oss.js
+2
@@ -73,6 +73,8 @@ export const enableUseMutableSource = false;
73
export const enableTransitionTracing = false;
74
75
export const enableFloat = false;
76
+
77
+export const useModernStrictMode = false;
78
// Flow magic to verify the exports of this file match the original version.
79
// eslint-disable-next-line no-unused-vars
80
type Check<_X, Y: _X, X: Y = _X> = null;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+2
@@ -73,6 +73,8 @@ export const enableUseMutableSource = false;
73
export const enableTransitionTracing = false;
74
75
export const enableFloat = false;
76
+
77
+export const useModernStrictMode = false;
78
// Flow magic to verify the exports of this file match the original version.
79
// eslint-disable-next-line no-unused-vars
80
type Check<_X, Y: _X, X: Y = _X> = null;
packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
+2
@@ -71,6 +71,8 @@ export const enableUseMutableSource = false;
71
export const enableTransitionTracing = false;
72
73
export const enableFloat = false;
74
+
75
+export const useModernStrictMode = false;
76
// Flow magic to verify the exports of this file match the original version.
77
// eslint-disable-next-line no-unused-vars
78
type Check<_X, Y: _X, X: Y = _X> = null;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+2
@@ -75,6 +75,8 @@ export const enableUseMutableSource = true;
75
export const enableTransitionTracing = false;
76
77
export const enableFloat = false;
78
+
79
+export const useModernStrictMode = false;
80
// Flow magic to verify the exports of this file match the original version.
81
// eslint-disable-next-line no-unused-vars
82
type Check<_X, Y: _X, X: Y = _X> = null;
packages/shared/forks/ReactFeatureFlags.testing.js
+2
@@ -73,6 +73,8 @@ export const enableUseMutableSource = false;
73
export const enableTransitionTracing = false;
74
75
export const enableFloat = false;
76
+
77
+export const useModernStrictMode = false;
78
// Flow magic to verify the exports of this file match the original version.
79
// eslint-disable-next-line no-unused-vars
80
type Check<_X, Y: _X, X: Y = _X> = null;
packages/shared/forks/ReactFeatureFlags.testing.www.js
+2
@@ -74,6 +74,8 @@ export const enableUseMutableSource = true;
74
export const enableTransitionTracing = false;
75
76
export const enableFloat = false;
77
+
78
+export const useModernStrictMode = false;
79
// Flow magic to verify the exports of this file match the original version.
80
// eslint-disable-next-line no-unused-vars
81
type Check<_X, Y: _X, X: Y = _X> = null;
packages/shared/forks/ReactFeatureFlags.www.js
+1
@@ -111,6 +111,7 @@ export const enableUseMutableSource = true;
111
112
export const enableCustomElementPropertySupport = __EXPERIMENTAL__;
113
114
+export const useModernStrictMode = false;
115
// Flow magic to verify the exports of this file match the original version.
116
// eslint-disable-next-line no-unused-vars
117
type Check<_X, Y: _X, X: Y = _X> = null;