diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js
index 84ef41f4fb97b..15705b59787bf 100644
--- a/packages/react-reconciler/src/ReactFiber.js
+++ b/packages/react-reconciler/src/ReactFiber.js
@@ -39,6 +39,7 @@ import {
enableDebugTracing,
enableFloat,
enableHostSingletons,
+ enableDO_NOT_USE_disableStrictPassiveEffect,
} from 'shared/ReactFeatureFlags';
import {NoFlags, Placement, StaticMask} from './ReactFiberFlags';
import {ConcurrentRoot} from './ReactRootTags';
@@ -87,6 +88,7 @@ import {
StrictLegacyMode,
StrictEffectsMode,
ConcurrentUpdatesByDefaultMode,
+ NoStrictPassiveEffectsMode,
} from './ReactTypeOfMode';
import {
REACT_FORWARD_REF_TYPE,
@@ -539,6 +541,12 @@ export function createFiberFromTypeAndProps(
if ((mode & ConcurrentMode) !== NoMode) {
// Strict effects should never run on legacy roots
mode |= StrictEffectsMode;
+ if (
+ enableDO_NOT_USE_disableStrictPassiveEffect &&
+ pendingProps.DO_NOT_USE_disableStrictPassiveEffect
+ ) {
+ mode |= NoStrictPassiveEffectsMode;
+ }
}
break;
case REACT_PROFILER_TYPE:
@@ -752,6 +760,10 @@ export function createFiberFromOffscreen(
lanes: Lanes,
key: null | string,
): Fiber {
+ if (__DEV__) {
+ // StrictMode in Offscreen should always run double passive effects
+ mode &= ~NoStrictPassiveEffectsMode;
+ }
const fiber = createFiber(OffscreenComponent, pendingProps, key, mode);
fiber.elementType = REACT_OFFSCREEN_TYPE;
fiber.lanes = lanes;
diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js
index 7d528709b6e52..53e4b39abd9da 100644
--- a/packages/react-reconciler/src/ReactFiberHooks.js
+++ b/packages/react-reconciler/src/ReactFiberHooks.js
@@ -58,6 +58,7 @@ import {
DebugTracingMode,
StrictEffectsMode,
StrictLegacyMode,
+ NoStrictPassiveEffectsMode,
} from './ReactTypeOfMode';
import {
NoLane,
@@ -2257,7 +2258,8 @@ function mountEffect(
): void {
if (
__DEV__ &&
- (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode
+ (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode &&
+ (currentlyRenderingFiber.mode & NoStrictPassiveEffectsMode) === NoMode
) {
mountEffectImpl(
MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect,
diff --git a/packages/react-reconciler/src/ReactTypeOfMode.js b/packages/react-reconciler/src/ReactTypeOfMode.js
index 07e08cff210bd..6da450ffeac6d 100644
--- a/packages/react-reconciler/src/ReactTypeOfMode.js
+++ b/packages/react-reconciler/src/ReactTypeOfMode.js
@@ -9,11 +9,12 @@
export type TypeOfMode = number;
-export const NoMode = /* */ 0b000000;
+export const NoMode = /* */ 0b0000000;
// TODO: Remove ConcurrentMode by reading from the root tag instead
-export const ConcurrentMode = /* */ 0b000001;
-export const ProfileMode = /* */ 0b000010;
-export const DebugTracingMode = /* */ 0b000100;
-export const StrictLegacyMode = /* */ 0b001000;
-export const StrictEffectsMode = /* */ 0b010000;
-export const ConcurrentUpdatesByDefaultMode = /* */ 0b100000;
+export const ConcurrentMode = /* */ 0b0000001;
+export const ProfileMode = /* */ 0b0000010;
+export const DebugTracingMode = /* */ 0b0000100;
+export const StrictLegacyMode = /* */ 0b0001000;
+export const StrictEffectsMode = /* */ 0b0010000;
+export const ConcurrentUpdatesByDefaultMode = /* */ 0b0100000;
+export const NoStrictPassiveEffectsMode = /* */ 0b1000000;
diff --git a/packages/react-reconciler/src/__tests__/ReactOffscreenStrictMode-test.js b/packages/react-reconciler/src/__tests__/ReactOffscreenStrictMode-test.js
index 129d4d0ddc8e0..f6ef02de522f9 100644
--- a/packages/react-reconciler/src/__tests__/ReactOffscreenStrictMode-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactOffscreenStrictMode-test.js
@@ -55,6 +55,30 @@ describe('ReactOffscreenStrictMode', () => {
]);
});
+ // @gate __DEV__ && enableOffscreen
+ it('should trigger strict effects when disableStrictPassiveEffect is presented on StrictMode', async () => {
+ await act(() => {
+ ReactNoop.render(
+
+
+
+
+ ,
+ );
+ });
+
+ expect(log).toEqual([
+ 'A: render',
+ 'A: render',
+ 'A: useLayoutEffect mount',
+ 'A: useEffect mount',
+ 'A: useLayoutEffect unmount',
+ 'A: useEffect unmount',
+ 'A: useLayoutEffect mount',
+ 'A: useEffect mount',
+ ]);
+ });
+
// @gate __DEV__ && enableOffscreen && useModernStrictMode
it('should not trigger strict effects when offscreen is hidden', async () => {
await act(() => {
diff --git a/packages/react/src/__tests__/ReactStrictMode-test.internal.js b/packages/react/src/__tests__/ReactStrictMode-test.internal.js
index 48efe5e7d5fd6..11ed37f599715 100644
--- a/packages/react/src/__tests__/ReactStrictMode-test.internal.js
+++ b/packages/react/src/__tests__/ReactStrictMode-test.internal.js
@@ -104,6 +104,28 @@ describe('ReactStrictMode', () => {
]);
});
+ // @gate enableDO_NOT_USE_disableStrictPassiveEffect
+ it('should include legacy + strict effects mode, but not strict passive effect with disableStrictPassiveEffect', async () => {
+ await act(() => {
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+ root.render(
+
+
+ ,
+ );
+ });
+
+ expect(log).toEqual([
+ 'A: render',
+ 'A: render',
+ 'A: useLayoutEffect mount',
+ 'A: useEffect mount',
+ 'A: useLayoutEffect unmount',
+ 'A: useLayoutEffect mount',
+ ]);
+ });
+
it('should allow level to be increased with nesting', async () => {
await act(() => {
const container = document.createElement('div');
diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js
index 7cb339c83cc85..1236429619ae8 100644
--- a/packages/shared/ReactFeatureFlags.js
+++ b/packages/shared/ReactFeatureFlags.js
@@ -237,3 +237,4 @@ export const consoleManagedByDevToolsDuringStrictMode = true;
// components will encounter in production, especially when used With .
// TODO: clean up legacy once tests pass WWW.
export const useModernStrictMode = false;
+export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js
index e96ce86b00286..0edbf1eea7072 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-fb.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js
@@ -82,6 +82,7 @@ export const enableFloat = true;
export const enableHostSingletons = true;
export const useModernStrictMode = false;
+export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
export const enableFizzExternalRuntime = false;
export const diffInCommitPhase = true;
diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js
index 97556a05514ce..1ad75cefa4954 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-oss.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js
@@ -68,6 +68,7 @@ export const enableFloat = true;
export const enableHostSingletons = true;
export const useModernStrictMode = false;
+export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
export const enableFizzExternalRuntime = false;
export const enableDeferRootSchedulingToMicrotask = true;
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
index 6437b4de07086..67d35fbdd1e85 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
@@ -68,6 +68,7 @@ export const enableFloat = true;
export const enableHostSingletons = true;
export const useModernStrictMode = false;
+export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
export const enableFizzExternalRuntime = false;
export const enableDeferRootSchedulingToMicrotask = true;
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
index 55535ce5e464d..0954b32e9af0b 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
@@ -66,6 +66,7 @@ export const enableFloat = true;
export const enableHostSingletons = true;
export const useModernStrictMode = false;
+export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
export const enableDeferRootSchedulingToMicrotask = true;
export const diffInCommitPhase = true;
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
index 1df2ff6b9eeea..00808854a4d2e 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
@@ -70,6 +70,7 @@ export const enableFloat = true;
export const enableHostSingletons = true;
export const useModernStrictMode = false;
+export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
export const enableFizzExternalRuntime = false;
export const enableDeferRootSchedulingToMicrotask = true;
diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js
index 58cfcdfff4214..a1197be405837 100644
--- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js
+++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js
@@ -28,6 +28,7 @@ export const enableDeferRootSchedulingToMicrotask = __VARIANT__;
export const diffInCommitPhase = __VARIANT__;
export const enableAsyncActions = __VARIANT__;
export const alwaysThrottleRetries = __VARIANT__;
+export const enableDO_NOT_USE_disableStrictPassiveEffect = __VARIANT__;
// Enable this flag to help with concurrent mode debugging.
// It logs information to the console about React scheduling, rendering, and commit phases.
diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js
index 426a9ec8013e6..36b5236df8319 100644
--- a/packages/shared/forks/ReactFeatureFlags.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.www.js
@@ -30,6 +30,7 @@ export const {
diffInCommitPhase,
enableAsyncActions,
alwaysThrottleRetries,
+ enableDO_NOT_USE_disableStrictPassiveEffect,
} = dynamicFeatureFlags;
// On WWW, __EXPERIMENTAL__ is used for a new modern build.