From 42f6a5e726f5d7783043c35485893a3db290f663 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 18 Feb 2021 16:30:20 -0500 Subject: [PATCH 1/5] The exported '' tag remains the same and opts legacy subtrees into strict mode level one ('mode == StrictModeL1'). This mode enables DEV-only double rendering, double component lifecycles, string ref warnings, legacy context warnings, etc. The primary purpose of this mode is to help detected render phase side effects. No new behavior. Roots created with experimental 'createRoot' and 'createBlockingRoot' APIs will also (for now) continue to default to strict mode level 1. In a subsequent commit I will add support for a 'level' attribute on the '' tag (as well as a new option supported by ). This will be the way to opt into strict mode level 2 ('mode == StrictModeL2'). This mode will enable DEV-only double invoking of effects on initial mount. This will simulate future Offscreen API semantics for trees being mounted, then hidden, and then shown again. The primary purpose of this mode is to enable applications to prepare for compatibility with the new Offscreen API (more information to follow shortly). For now, this commit changes no public facing behavior. The only mechanism for opting into strict mode level 2 is the pre-existing 'enableDoubleInvokingEffects' feature flag (only enabled within Facebook for now). --- .../src/ReactChildFiber.new.js | 4 ++-- .../src/ReactChildFiber.old.js | 4 ++-- .../react-reconciler/src/ReactFiber.new.js | 21 ++++++++++++++----- .../react-reconciler/src/ReactFiber.old.js | 21 ++++++++++++++----- .../src/ReactFiberBeginWork.new.js | 12 +++++------ .../src/ReactFiberBeginWork.old.js | 12 +++++------ .../src/ReactFiberClassComponent.new.js | 21 +++++++++---------- .../src/ReactFiberClassComponent.old.js | 21 +++++++++---------- .../src/ReactFiberCommitWork.new.js | 16 +++++++------- .../src/ReactFiberCommitWork.old.js | 16 +++++++------- .../src/ReactFiberHooks.new.js | 16 ++++++++------ .../src/ReactFiberHooks.old.js | 16 ++++++++------ .../src/ReactFiberReconciler.new.js | 6 +++--- .../src/ReactFiberReconciler.old.js | 6 +++--- .../src/ReactFiberWorkLoop.new.js | 13 ++++++------ .../src/ReactFiberWorkLoop.old.js | 13 ++++++------ .../src/ReactStrictModeWarnings.new.js | 10 ++++----- .../src/ReactStrictModeWarnings.old.js | 10 ++++----- .../react-reconciler/src/ReactTypeOfMode.js | 16 +++++++------- .../src/ReactUpdateQueue.new.js | 6 +++--- .../src/ReactUpdateQueue.old.js | 6 +++--- packages/shared/ReactFeatureFlags.js | 8 ++++--- 22 files changed, 153 insertions(+), 121 deletions(-) diff --git a/packages/react-reconciler/src/ReactChildFiber.new.js b/packages/react-reconciler/src/ReactChildFiber.new.js index 3435694b7ab0a..760c4abafa384 100644 --- a/packages/react-reconciler/src/ReactChildFiber.new.js +++ b/packages/react-reconciler/src/ReactChildFiber.new.js @@ -46,7 +46,7 @@ import { } from './ReactFiber.new'; import {emptyRefsObject} from './ReactFiberClassComponent.new'; import {isCompatibleFamilyForHotReloading} from './ReactFiberHotReloading.new'; -import {StrictMode} from './ReactTypeOfMode'; +import {StrictModeL1} from './ReactTypeOfMode'; let didWarnAboutMaps; let didWarnAboutGenerators; @@ -114,7 +114,7 @@ function coerceRef( // TODO: Clean this up once we turn on the string ref warning for // everyone, because the strict mode case will no longer be relevant if ( - (returnFiber.mode & StrictMode || warnAboutStringRefs) && + (returnFiber.mode & StrictModeL1 || warnAboutStringRefs) && // We warn in ReactElement.js if owner and self are equal for string refs // because these cannot be automatically converted to an arrow function // using a codemod. Therefore, we don't have to warn about string refs again. diff --git a/packages/react-reconciler/src/ReactChildFiber.old.js b/packages/react-reconciler/src/ReactChildFiber.old.js index 7a4ab0b172085..b9cdb22ec2bf2 100644 --- a/packages/react-reconciler/src/ReactChildFiber.old.js +++ b/packages/react-reconciler/src/ReactChildFiber.old.js @@ -46,7 +46,7 @@ import { } from './ReactFiber.old'; import {emptyRefsObject} from './ReactFiberClassComponent.old'; import {isCompatibleFamilyForHotReloading} from './ReactFiberHotReloading.old'; -import {StrictMode} from './ReactTypeOfMode'; +import {StrictModeL1} from './ReactTypeOfMode'; let didWarnAboutMaps; let didWarnAboutGenerators; @@ -114,7 +114,7 @@ function coerceRef( // TODO: Clean this up once we turn on the string ref warning for // everyone, because the strict mode case will no longer be relevant if ( - (returnFiber.mode & StrictMode || warnAboutStringRefs) && + (returnFiber.mode & StrictModeL1 || warnAboutStringRefs) && // We warn in ReactElement.js if owner and self are equal for string refs // because these cannot be automatically converted to an arrow function // using a codemod. Therefore, we don't have to warn about string refs again. diff --git a/packages/react-reconciler/src/ReactFiber.new.js b/packages/react-reconciler/src/ReactFiber.new.js index 983855ced5685..9728549a16e42 100644 --- a/packages/react-reconciler/src/ReactFiber.new.js +++ b/packages/react-reconciler/src/ReactFiber.new.js @@ -19,9 +19,10 @@ import type {OffscreenProps} from './ReactFiberOffscreenComponent'; import invariant from 'shared/invariant'; import { + enableCache, + enableDoubleInvokingEffects, enableProfilerTimer, enableScopeAPI, - enableCache, } from 'shared/ReactFeatureFlags'; import {NoFlags, Placement, StaticMask} from './ReactFiberFlags'; import {ConcurrentRoot, BlockingRoot} from './ReactRootTags'; @@ -64,7 +65,8 @@ import { ConcurrentMode, DebugTracingMode, ProfileMode, - StrictMode, + StrictModeL1, + StrictModeL2, BlockingMode, } from './ReactTypeOfMode'; import { @@ -421,9 +423,17 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) { export function createHostRootFiber(tag: RootTag): Fiber { let mode; if (tag === ConcurrentRoot) { - mode = ConcurrentMode | BlockingMode | StrictMode; + if (enableDoubleInvokingEffects) { + mode = ConcurrentMode | BlockingMode | StrictModeL1 | StrictModeL2; + } else { + mode = ConcurrentMode | BlockingMode | StrictModeL1; + } } else if (tag === BlockingRoot) { - mode = BlockingMode | StrictMode; + if (enableDoubleInvokingEffects) { + mode = BlockingMode | StrictModeL1 | StrictModeL2; + } else { + mode = BlockingMode | StrictModeL1; + } } else { mode = NoMode; } @@ -472,7 +482,8 @@ export function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = Mode; - mode |= StrictMode; + // TODO (StrictModeL2) Add support for new strict mode "level" attribute + mode |= StrictModeL1; break; case REACT_PROFILER_TYPE: return createFiberFromProfiler(pendingProps, mode, lanes, key); diff --git a/packages/react-reconciler/src/ReactFiber.old.js b/packages/react-reconciler/src/ReactFiber.old.js index ed2ae45f4ec17..3edeb253f1b26 100644 --- a/packages/react-reconciler/src/ReactFiber.old.js +++ b/packages/react-reconciler/src/ReactFiber.old.js @@ -19,9 +19,10 @@ import type {OffscreenProps} from './ReactFiberOffscreenComponent'; import invariant from 'shared/invariant'; import { + enableCache, + enableDoubleInvokingEffects, enableProfilerTimer, enableScopeAPI, - enableCache, } from 'shared/ReactFeatureFlags'; import {NoFlags, Placement, StaticMask} from './ReactFiberFlags'; import {ConcurrentRoot, BlockingRoot} from './ReactRootTags'; @@ -64,7 +65,8 @@ import { ConcurrentMode, DebugTracingMode, ProfileMode, - StrictMode, + StrictModeL1, + StrictModeL2, BlockingMode, } from './ReactTypeOfMode'; import { @@ -421,9 +423,17 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) { export function createHostRootFiber(tag: RootTag): Fiber { let mode; if (tag === ConcurrentRoot) { - mode = ConcurrentMode | BlockingMode | StrictMode; + if (enableDoubleInvokingEffects) { + mode = ConcurrentMode | BlockingMode | StrictModeL1 | StrictModeL2; + } else { + mode = ConcurrentMode | BlockingMode | StrictModeL1; + } } else if (tag === BlockingRoot) { - mode = BlockingMode | StrictMode; + if (enableDoubleInvokingEffects) { + mode = BlockingMode | StrictModeL1 | StrictModeL2; + } else { + mode = BlockingMode | StrictModeL1; + } } else { mode = NoMode; } @@ -472,7 +482,8 @@ export function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = Mode; - mode |= StrictMode; + // TODO (StrictModeL2) Add support for new strict mode "level" attribute + mode |= StrictModeL1; break; case REACT_PROFILER_TYPE: return createFiberFromProfiler(pendingProps, mode, lanes, key); diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index 6537d103e2324..3c368e562b6c0 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -125,7 +125,7 @@ import { ConcurrentMode, NoMode, ProfileMode, - StrictMode, + StrictModeL1, BlockingMode, } from './ReactTypeOfMode'; import { @@ -357,7 +357,7 @@ function updateForwardRef( ); if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -889,7 +889,7 @@ function updateFunctionComponent( ); if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -1068,7 +1068,7 @@ function finishClassComponent( nextChildren = instance.render(); if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -1478,7 +1478,7 @@ function mountIndeterminateComponent( } } - if (workInProgress.mode & StrictMode) { + if (workInProgress.mode & StrictModeL1) { ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); } @@ -1615,7 +1615,7 @@ function mountIndeterminateComponent( if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index 4c0594329d94e..112371a593f90 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -125,7 +125,7 @@ import { ConcurrentMode, NoMode, ProfileMode, - StrictMode, + StrictModeL1, BlockingMode, } from './ReactTypeOfMode'; import { @@ -357,7 +357,7 @@ function updateForwardRef( ); if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -889,7 +889,7 @@ function updateFunctionComponent( ); if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -1068,7 +1068,7 @@ function finishClassComponent( nextChildren = instance.render(); if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -1478,7 +1478,7 @@ function mountIndeterminateComponent( } } - if (workInProgress.mode & StrictMode) { + if (workInProgress.mode & StrictModeL1) { ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); } @@ -1615,7 +1615,7 @@ function mountIndeterminateComponent( if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.new.js b/packages/react-reconciler/src/ReactFiberClassComponent.new.js index 389b4877852db..f2663d42b176e 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.new.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.new.js @@ -31,11 +31,10 @@ import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols'; import {resolveDefaultProps} from './ReactFiberLazyComponent.new'; import { - BlockingMode, - ConcurrentMode, DebugTracingMode, NoMode, - StrictMode, + StrictModeL1, + StrictModeL2, } from './ReactTypeOfMode'; import { @@ -165,7 +164,7 @@ export function applyDerivedStateFromProps( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -318,7 +317,7 @@ function checkShouldComponentUpdate( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -655,7 +654,7 @@ function constructClassInstance( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -862,7 +861,7 @@ function mountClassInstance( } } - if (workInProgress.mode & StrictMode) { + if (workInProgress.mode & StrictModeL1) { ReactStrictModeWarnings.recordLegacyContextWarning( workInProgress, instance, @@ -910,7 +909,7 @@ function mountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (workInProgress.mode & StrictModeL2) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; @@ -989,7 +988,7 @@ function resumeMountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (workInProgress.mode & StrictModeL2) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; @@ -1041,7 +1040,7 @@ function resumeMountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (workInProgress.mode & StrictModeL2) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; @@ -1056,7 +1055,7 @@ function resumeMountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (workInProgress.mode & StrictModeL2) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.old.js b/packages/react-reconciler/src/ReactFiberClassComponent.old.js index 5f366e7c253a2..ea8e85d7c2a88 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.old.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.old.js @@ -31,11 +31,10 @@ import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols'; import {resolveDefaultProps} from './ReactFiberLazyComponent.old'; import { - BlockingMode, - ConcurrentMode, DebugTracingMode, NoMode, - StrictMode, + StrictModeL1, + StrictModeL2, } from './ReactTypeOfMode'; import { @@ -165,7 +164,7 @@ export function applyDerivedStateFromProps( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -318,7 +317,7 @@ function checkShouldComponentUpdate( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -655,7 +654,7 @@ function constructClassInstance( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -862,7 +861,7 @@ function mountClassInstance( } } - if (workInProgress.mode & StrictMode) { + if (workInProgress.mode & StrictModeL1) { ReactStrictModeWarnings.recordLegacyContextWarning( workInProgress, instance, @@ -910,7 +909,7 @@ function mountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (workInProgress.mode & StrictModeL2) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; @@ -989,7 +988,7 @@ function resumeMountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (workInProgress.mode & StrictModeL2) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; @@ -1041,7 +1040,7 @@ function resumeMountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (workInProgress.mode & StrictModeL2) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; @@ -1056,7 +1055,7 @@ function resumeMountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (workInProgress.mode & StrictModeL2) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.new.js b/packages/react-reconciler/src/ReactFiberCommitWork.new.js index e170114848cc8..85dcdda5be32d 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.new.js @@ -2476,8 +2476,8 @@ function ensureCorrectReturnPointer(fiber, expectedReturnFiber) { function invokeLayoutEffectMountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check for legacy roots here. - // This function will not be called within legacy roots. + // We don't need to re-check StrictModeL2 here. + // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: case ForwardRef: @@ -2510,8 +2510,8 @@ function invokeLayoutEffectMountInDEV(fiber: Fiber): void { function invokePassiveEffectMountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check for legacy roots here. - // This function will not be called within legacy roots. + // We don't need to re-check StrictModeL2 here. + // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: case ForwardRef: @@ -2535,8 +2535,8 @@ function invokePassiveEffectMountInDEV(fiber: Fiber): void { function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check for legacy roots here. - // This function will not be called within legacy roots. + // We don't need to re-check StrictModeL2 here. + // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: case ForwardRef: @@ -2579,8 +2579,8 @@ function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void { function invokePassiveEffectUnmountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check for legacy roots here. - // This function will not be called within legacy roots. + // We don't need to re-check StrictModeL2 here. + // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: case ForwardRef: diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.old.js b/packages/react-reconciler/src/ReactFiberCommitWork.old.js index 7a27bbc48f875..cae91bc7cfed2 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.old.js @@ -2476,8 +2476,8 @@ function ensureCorrectReturnPointer(fiber, expectedReturnFiber) { function invokeLayoutEffectMountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check for legacy roots here. - // This function will not be called within legacy roots. + // We don't need to re-check StrictModeL2 here. + // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: case ForwardRef: @@ -2510,8 +2510,8 @@ function invokeLayoutEffectMountInDEV(fiber: Fiber): void { function invokePassiveEffectMountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check for legacy roots here. - // This function will not be called within legacy roots. + // We don't need to re-check StrictModeL2 here. + // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: case ForwardRef: @@ -2535,8 +2535,8 @@ function invokePassiveEffectMountInDEV(fiber: Fiber): void { function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check for legacy roots here. - // This function will not be called within legacy roots. + // We don't need to re-check StrictModeL2 here. + // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: case ForwardRef: @@ -2579,8 +2579,8 @@ function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void { function invokePassiveEffectUnmountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check for legacy roots here. - // This function will not be called within legacy roots. + // We don't need to re-check StrictModeL2 here. + // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: case ForwardRef: diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 32144cc1b0505..5168040e25530 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -35,8 +35,8 @@ import { import { NoMode, BlockingMode, - ConcurrentMode, DebugTracingMode, + StrictModeL2, } from './ReactTypeOfMode'; import { NoLane, @@ -510,7 +510,7 @@ export function bailoutHooks( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (workInProgress.mode & StrictModeL2) !== NoMode ) { workInProgress.flags &= ~( MountPassiveDevEffect | @@ -1424,7 +1424,7 @@ function mountEffect( if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (currentlyRenderingFiber.mode & StrictModeL2) !== NoMode ) { return mountEffectImpl( MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect, @@ -1462,7 +1462,7 @@ function mountLayoutEffect( if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (currentlyRenderingFiber.mode & StrictModeL2) !== NoMode ) { return mountEffectImpl( MountLayoutDevEffect | UpdateEffect, @@ -1534,7 +1534,7 @@ function mountImperativeHandle( if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (currentlyRenderingFiber.mode & StrictModeL2) !== NoMode ) { return mountEffectImpl( MountLayoutDevEffect | UpdateEffect, @@ -1830,7 +1830,11 @@ function mountOpaqueIdentifier(): OpaqueIDType | void { const setId = mountState(id)[1]; if ((currentlyRenderingFiber.mode & BlockingMode) === NoMode) { - if (__DEV__ && enableDoubleInvokingEffects) { + if ( + __DEV__ && + enableDoubleInvokingEffects && + (currentlyRenderingFiber.mode & StrictModeL2) === NoMode + ) { currentlyRenderingFiber.flags |= MountPassiveDevEffect | PassiveEffect; } else { currentlyRenderingFiber.flags |= PassiveEffect; diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index ce0ac36636562..91375d4702243 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -35,8 +35,8 @@ import { import { NoMode, BlockingMode, - ConcurrentMode, DebugTracingMode, + StrictModeL2, } from './ReactTypeOfMode'; import { NoLane, @@ -510,7 +510,7 @@ export function bailoutHooks( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (workInProgress.mode & StrictModeL2) !== NoMode ) { workInProgress.flags &= ~( MountPassiveDevEffect | @@ -1424,7 +1424,7 @@ function mountEffect( if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (currentlyRenderingFiber.mode & StrictModeL2) !== NoMode ) { return mountEffectImpl( MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect, @@ -1462,7 +1462,7 @@ function mountLayoutEffect( if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (currentlyRenderingFiber.mode & StrictModeL2) !== NoMode ) { return mountEffectImpl( MountLayoutDevEffect | UpdateEffect, @@ -1534,7 +1534,7 @@ function mountImperativeHandle( if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & (BlockingMode | ConcurrentMode)) !== NoMode + (currentlyRenderingFiber.mode & StrictModeL2) !== NoMode ) { return mountEffectImpl( MountLayoutDevEffect | UpdateEffect, @@ -1830,7 +1830,11 @@ function mountOpaqueIdentifier(): OpaqueIDType | void { const setId = mountState(id)[1]; if ((currentlyRenderingFiber.mode & BlockingMode) === NoMode) { - if (__DEV__ && enableDoubleInvokingEffects) { + if ( + __DEV__ && + enableDoubleInvokingEffects && + (currentlyRenderingFiber.mode & StrictModeL2) === NoMode + ) { currentlyRenderingFiber.flags |= MountPassiveDevEffect | PassiveEffect; } else { currentlyRenderingFiber.flags |= PassiveEffect; diff --git a/packages/react-reconciler/src/ReactFiberReconciler.new.js b/packages/react-reconciler/src/ReactFiberReconciler.new.js index be5ee15970dc7..64e8aa30f7bd8 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.new.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.new.js @@ -75,7 +75,7 @@ import { resetCurrentFiber as resetCurrentDebugFiberInDEV, setCurrentFiber as setCurrentDebugFiberInDEV, } from './ReactCurrentFiber'; -import {StrictMode} from './ReactTypeOfMode'; +import {StrictModeL1} from './ReactTypeOfMode'; import { SyncLane, InputDiscreteHydrationLane, @@ -204,7 +204,7 @@ function findHostInstanceWithWarning( if (hostFiber === null) { return null; } - if (hostFiber.mode & StrictMode) { + if (hostFiber.mode & StrictModeL1) { const componentName = getComponentName(fiber.type) || 'Component'; if (!didWarnAboutFindNodeInStrictMode[componentName]) { didWarnAboutFindNodeInStrictMode[componentName] = true; @@ -212,7 +212,7 @@ function findHostInstanceWithWarning( const previousFiber = ReactCurrentFiberCurrent; try { setCurrentDebugFiberInDEV(hostFiber); - if (fiber.mode & StrictMode) { + if (fiber.mode & StrictModeL1) { console.error( '%s is deprecated in StrictMode. ' + '%s was passed an instance of %s which is inside StrictMode. ' + diff --git a/packages/react-reconciler/src/ReactFiberReconciler.old.js b/packages/react-reconciler/src/ReactFiberReconciler.old.js index 77032eb24b108..f4207586aab5e 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.old.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.old.js @@ -75,7 +75,7 @@ import { resetCurrentFiber as resetCurrentDebugFiberInDEV, setCurrentFiber as setCurrentDebugFiberInDEV, } from './ReactCurrentFiber'; -import {StrictMode} from './ReactTypeOfMode'; +import {StrictModeL1} from './ReactTypeOfMode'; import { SyncLane, InputDiscreteHydrationLane, @@ -204,7 +204,7 @@ function findHostInstanceWithWarning( if (hostFiber === null) { return null; } - if (hostFiber.mode & StrictMode) { + if (hostFiber.mode & StrictModeL1) { const componentName = getComponentName(fiber.type) || 'Component'; if (!didWarnAboutFindNodeInStrictMode[componentName]) { didWarnAboutFindNodeInStrictMode[componentName] = true; @@ -212,7 +212,7 @@ function findHostInstanceWithWarning( const previousFiber = ReactCurrentFiberCurrent; try { setCurrentDebugFiberInDEV(hostFiber); - if (fiber.mode & StrictMode) { + if (fiber.mode & StrictModeL1) { console.error( '%s is deprecated in StrictMode. ' + '%s was passed an instance of %s which is inside StrictMode. ' + diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 918261a2e3562..68558b6160cc5 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -104,7 +104,8 @@ import { } from './ReactFiber.new'; import { NoMode, - StrictMode, + StrictModeL1, + StrictModeL2, ProfileMode, BlockingMode, ConcurrentMode, @@ -2561,8 +2562,8 @@ function commitDoubleInvokeEffectsInDEV( hasPassiveEffects: boolean, ) { if (__DEV__ && enableDoubleInvokingEffects) { - // Never double-invoke effects for legacy roots. - if ((fiber.mode & (BlockingMode | ConcurrentMode)) === NoMode) { + // Never double-invoke effects outside of StrictModeL2. + if ((fiber.mode & StrictModeL2) === NoMode) { return; } @@ -2590,8 +2591,8 @@ function invokeEffectsInDev( invokeEffectFn: (fiber: Fiber) => void, ): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check for legacy roots here. - // This function will not be called within legacy roots. + // We don't need to re-check StrictModeL2 here. + // This function is only called if that check has already passed. let current = firstChild; let subtreeRoot = null; @@ -2934,7 +2935,7 @@ export function warnIfNotCurrentlyActingEffectsInDEV(fiber: Fiber): void { if (__DEV__) { if ( warnsIfNotActing === true && - (fiber.mode & StrictMode) !== NoMode && + (fiber.mode & StrictModeL1) !== NoMode && IsSomeRendererActing.current === false && IsThisRendererActing.current === false ) { diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 12ef384a47527..35906c1412b8b 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -104,7 +104,8 @@ import { } from './ReactFiber.old'; import { NoMode, - StrictMode, + StrictModeL1, + StrictModeL2, ProfileMode, BlockingMode, ConcurrentMode, @@ -2561,8 +2562,8 @@ function commitDoubleInvokeEffectsInDEV( hasPassiveEffects: boolean, ) { if (__DEV__ && enableDoubleInvokingEffects) { - // Never double-invoke effects for legacy roots. - if ((fiber.mode & (BlockingMode | ConcurrentMode)) === NoMode) { + // Never double-invoke effects outside of StrictModeL2. + if ((fiber.mode & StrictModeL2) === NoMode) { return; } @@ -2590,8 +2591,8 @@ function invokeEffectsInDev( invokeEffectFn: (fiber: Fiber) => void, ): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check for legacy roots here. - // This function will not be called within legacy roots. + // We don't need to re-check StrictModeL2 here. + // This function is only called if that check has already passed. let current = firstChild; let subtreeRoot = null; @@ -2934,7 +2935,7 @@ export function warnIfNotCurrentlyActingEffectsInDEV(fiber: Fiber): void { if (__DEV__) { if ( warnsIfNotActing === true && - (fiber.mode & StrictMode) !== NoMode && + (fiber.mode & StrictModeL1) !== NoMode && IsSomeRendererActing.current === false && IsThisRendererActing.current === false ) { diff --git a/packages/react-reconciler/src/ReactStrictModeWarnings.new.js b/packages/react-reconciler/src/ReactStrictModeWarnings.new.js index 5dd09a8cc80da..865179a0792d2 100644 --- a/packages/react-reconciler/src/ReactStrictModeWarnings.new.js +++ b/packages/react-reconciler/src/ReactStrictModeWarnings.new.js @@ -14,7 +14,7 @@ import { setCurrentFiber as setCurrentDebugFiberInDEV, } from './ReactCurrentFiber'; import getComponentName from 'shared/getComponentName'; -import {StrictMode} from './ReactTypeOfMode'; +import {StrictModeL1} from './ReactTypeOfMode'; type FiberArray = Array; type FiberToFiberComponentsMap = Map; @@ -33,7 +33,7 @@ if (__DEV__) { let node = fiber; while (node !== null) { - if (node.mode & StrictMode) { + if (node.mode & StrictModeL1) { maybeStrictRoot = node; } node = node.return; @@ -78,7 +78,7 @@ if (__DEV__) { } if ( - fiber.mode & StrictMode && + fiber.mode & StrictModeL1 && typeof instance.UNSAFE_componentWillMount === 'function' ) { pendingUNSAFE_ComponentWillMountWarnings.push(fiber); @@ -92,7 +92,7 @@ if (__DEV__) { } if ( - fiber.mode & StrictMode && + fiber.mode & StrictModeL1 && typeof instance.UNSAFE_componentWillReceiveProps === 'function' ) { pendingUNSAFE_ComponentWillReceivePropsWarnings.push(fiber); @@ -106,7 +106,7 @@ if (__DEV__) { } if ( - fiber.mode & StrictMode && + fiber.mode & StrictModeL1 && typeof instance.UNSAFE_componentWillUpdate === 'function' ) { pendingUNSAFE_ComponentWillUpdateWarnings.push(fiber); diff --git a/packages/react-reconciler/src/ReactStrictModeWarnings.old.js b/packages/react-reconciler/src/ReactStrictModeWarnings.old.js index 5dd09a8cc80da..865179a0792d2 100644 --- a/packages/react-reconciler/src/ReactStrictModeWarnings.old.js +++ b/packages/react-reconciler/src/ReactStrictModeWarnings.old.js @@ -14,7 +14,7 @@ import { setCurrentFiber as setCurrentDebugFiberInDEV, } from './ReactCurrentFiber'; import getComponentName from 'shared/getComponentName'; -import {StrictMode} from './ReactTypeOfMode'; +import {StrictModeL1} from './ReactTypeOfMode'; type FiberArray = Array; type FiberToFiberComponentsMap = Map; @@ -33,7 +33,7 @@ if (__DEV__) { let node = fiber; while (node !== null) { - if (node.mode & StrictMode) { + if (node.mode & StrictModeL1) { maybeStrictRoot = node; } node = node.return; @@ -78,7 +78,7 @@ if (__DEV__) { } if ( - fiber.mode & StrictMode && + fiber.mode & StrictModeL1 && typeof instance.UNSAFE_componentWillMount === 'function' ) { pendingUNSAFE_ComponentWillMountWarnings.push(fiber); @@ -92,7 +92,7 @@ if (__DEV__) { } if ( - fiber.mode & StrictMode && + fiber.mode & StrictModeL1 && typeof instance.UNSAFE_componentWillReceiveProps === 'function' ) { pendingUNSAFE_ComponentWillReceivePropsWarnings.push(fiber); @@ -106,7 +106,7 @@ if (__DEV__) { } if ( - fiber.mode & StrictMode && + fiber.mode & StrictModeL1 && typeof instance.UNSAFE_componentWillUpdate === 'function' ) { pendingUNSAFE_ComponentWillUpdateWarnings.push(fiber); diff --git a/packages/react-reconciler/src/ReactTypeOfMode.js b/packages/react-reconciler/src/ReactTypeOfMode.js index f6092058d2ba0..7025d3d1f5877 100644 --- a/packages/react-reconciler/src/ReactTypeOfMode.js +++ b/packages/react-reconciler/src/ReactTypeOfMode.js @@ -9,11 +9,11 @@ export type TypeOfMode = number; -export const NoMode = 0b00000; -export const StrictMode = 0b00001; -// TODO: Remove BlockingMode and ConcurrentMode by reading from the root -// tag instead -export const BlockingMode = 0b00010; -export const ConcurrentMode = 0b00100; -export const ProfileMode = 0b01000; -export const DebugTracingMode = 0b10000; +export const NoMode = /* */ 0b000000; +// TODO: Remove BlockingMode and ConcurrentMode by reading from the root tag instead +export const BlockingMode = /* */ 0b000001; +export const ConcurrentMode = /* */ 0b000010; +export const ProfileMode = /* */ 0b000100; +export const DebugTracingMode = /* */ 0b001000; +export const StrictModeL1 = /* */ 0b010000; +export const StrictModeL2 = /* */ 0b100000; diff --git a/packages/react-reconciler/src/ReactUpdateQueue.new.js b/packages/react-reconciler/src/ReactUpdateQueue.new.js index bb4e9e1bc52dc..46c9e6026f130 100644 --- a/packages/react-reconciler/src/ReactUpdateQueue.new.js +++ b/packages/react-reconciler/src/ReactUpdateQueue.new.js @@ -104,7 +104,7 @@ import {Callback, ShouldCapture, DidCapture} from './ReactFiberFlags'; import {debugRenderPhaseSideEffectsForStrictMode} from 'shared/ReactFeatureFlags'; -import {StrictMode} from './ReactTypeOfMode'; +import {StrictModeL1} from './ReactTypeOfMode'; import { markSkippedUpdateLanes, isInterleavedUpdate, @@ -392,7 +392,7 @@ function getStateFromUpdate( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -425,7 +425,7 @@ function getStateFromUpdate( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { diff --git a/packages/react-reconciler/src/ReactUpdateQueue.old.js b/packages/react-reconciler/src/ReactUpdateQueue.old.js index 6b578ef9c80d6..8b4a6b2bff67b 100644 --- a/packages/react-reconciler/src/ReactUpdateQueue.old.js +++ b/packages/react-reconciler/src/ReactUpdateQueue.old.js @@ -104,7 +104,7 @@ import {Callback, ShouldCapture, DidCapture} from './ReactFiberFlags'; import {debugRenderPhaseSideEffectsForStrictMode} from 'shared/ReactFeatureFlags'; -import {StrictMode} from './ReactTypeOfMode'; +import {StrictModeL1} from './ReactTypeOfMode'; import { markSkippedUpdateLanes, isInterleavedUpdate, @@ -392,7 +392,7 @@ function getStateFromUpdate( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { @@ -425,7 +425,7 @@ function getStateFromUpdate( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode + workInProgress.mode & StrictModeL1 ) { disableLogs(); try { diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 778881bc5837d..331989800783b 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -20,9 +20,13 @@ export const enableDebugTracing = false; export const enableSchedulingProfiler = __PROFILE__ && __EXPERIMENTAL__; // Helps identify side effects in render-phase lifecycle hooks and setState -// reducers by double invoking them in Strict Mode. +// reducers by double invoking them in StrictModeL1. export const debugRenderPhaseSideEffectsForStrictMode = __DEV__; +// Helps identify code that is not safe for planned Offscreen API and Suspense semantics; +// this feature flag only impacts StrictModeL2. +export const enableDoubleInvokingEffects = false; + // To preserve the "Pause on caught exceptions" behavior of the debugger, we // replay the begin phase of a failed component inside invokeGuardedCallback. export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; @@ -139,8 +143,6 @@ export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableDoubleInvokingEffects = false; - export const enableUseRefAccessWarning = false; export const enableRecursiveCommitTraversal = false; From 372bb8b3e819485effd4a01c876423dffc3eaf9e Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 19 Feb 2021 11:33:22 -0500 Subject: [PATCH 2/5] Renamed strict mode constants StrictModeL1 -> StrictLegacyMode and StrictModeL2 -> StrictEffectsMode --- .../src/ReactChildFiber.new.js | 4 ++-- .../src/ReactChildFiber.old.js | 4 ++-- .../react-reconciler/src/ReactFiber.new.js | 17 ++++++++-------- .../react-reconciler/src/ReactFiber.old.js | 17 ++++++++-------- .../src/ReactFiberBeginWork.new.js | 12 +++++------ .../src/ReactFiberBeginWork.old.js | 12 +++++------ .../src/ReactFiberClassComponent.new.js | 20 +++++++++---------- .../src/ReactFiberClassComponent.old.js | 20 +++++++++---------- .../src/ReactFiberCommitWork.new.js | 8 ++++---- .../src/ReactFiberCommitWork.old.js | 8 ++++---- .../src/ReactFiberHooks.new.js | 12 +++++------ .../src/ReactFiberHooks.old.js | 12 +++++------ .../src/ReactFiberReconciler.new.js | 6 +++--- .../src/ReactFiberReconciler.old.js | 6 +++--- .../src/ReactFiberWorkLoop.new.js | 12 +++++------ .../src/ReactFiberWorkLoop.old.js | 12 +++++------ .../src/ReactStrictModeWarnings.new.js | 10 +++++----- .../src/ReactStrictModeWarnings.old.js | 10 +++++----- .../react-reconciler/src/ReactTypeOfMode.js | 14 ++++++------- .../src/ReactUpdateQueue.new.js | 6 +++--- .../src/ReactUpdateQueue.old.js | 6 +++--- packages/shared/ReactFeatureFlags.js | 4 ++-- 22 files changed, 117 insertions(+), 115 deletions(-) diff --git a/packages/react-reconciler/src/ReactChildFiber.new.js b/packages/react-reconciler/src/ReactChildFiber.new.js index 760c4abafa384..222a8730c4fa2 100644 --- a/packages/react-reconciler/src/ReactChildFiber.new.js +++ b/packages/react-reconciler/src/ReactChildFiber.new.js @@ -46,7 +46,7 @@ import { } from './ReactFiber.new'; import {emptyRefsObject} from './ReactFiberClassComponent.new'; import {isCompatibleFamilyForHotReloading} from './ReactFiberHotReloading.new'; -import {StrictModeL1} from './ReactTypeOfMode'; +import {StrictLegacyMode} from './ReactTypeOfMode'; let didWarnAboutMaps; let didWarnAboutGenerators; @@ -114,7 +114,7 @@ function coerceRef( // TODO: Clean this up once we turn on the string ref warning for // everyone, because the strict mode case will no longer be relevant if ( - (returnFiber.mode & StrictModeL1 || warnAboutStringRefs) && + (returnFiber.mode & StrictLegacyMode || warnAboutStringRefs) && // We warn in ReactElement.js if owner and self are equal for string refs // because these cannot be automatically converted to an arrow function // using a codemod. Therefore, we don't have to warn about string refs again. diff --git a/packages/react-reconciler/src/ReactChildFiber.old.js b/packages/react-reconciler/src/ReactChildFiber.old.js index b9cdb22ec2bf2..036fa65503c04 100644 --- a/packages/react-reconciler/src/ReactChildFiber.old.js +++ b/packages/react-reconciler/src/ReactChildFiber.old.js @@ -46,7 +46,7 @@ import { } from './ReactFiber.old'; import {emptyRefsObject} from './ReactFiberClassComponent.old'; import {isCompatibleFamilyForHotReloading} from './ReactFiberHotReloading.old'; -import {StrictModeL1} from './ReactTypeOfMode'; +import {StrictLegacyMode} from './ReactTypeOfMode'; let didWarnAboutMaps; let didWarnAboutGenerators; @@ -114,7 +114,7 @@ function coerceRef( // TODO: Clean this up once we turn on the string ref warning for // everyone, because the strict mode case will no longer be relevant if ( - (returnFiber.mode & StrictModeL1 || warnAboutStringRefs) && + (returnFiber.mode & StrictLegacyMode || warnAboutStringRefs) && // We warn in ReactElement.js if owner and self are equal for string refs // because these cannot be automatically converted to an arrow function // using a codemod. Therefore, we don't have to warn about string refs again. diff --git a/packages/react-reconciler/src/ReactFiber.new.js b/packages/react-reconciler/src/ReactFiber.new.js index 9728549a16e42..69f1ab744498c 100644 --- a/packages/react-reconciler/src/ReactFiber.new.js +++ b/packages/react-reconciler/src/ReactFiber.new.js @@ -65,8 +65,8 @@ import { ConcurrentMode, DebugTracingMode, ProfileMode, - StrictModeL1, - StrictModeL2, + StrictLegacyMode, + StrictEffectsMode, BlockingMode, } from './ReactTypeOfMode'; import { @@ -424,15 +424,16 @@ export function createHostRootFiber(tag: RootTag): Fiber { let mode; if (tag === ConcurrentRoot) { if (enableDoubleInvokingEffects) { - mode = ConcurrentMode | BlockingMode | StrictModeL1 | StrictModeL2; + mode = + ConcurrentMode | BlockingMode | StrictLegacyMode | StrictEffectsMode; } else { - mode = ConcurrentMode | BlockingMode | StrictModeL1; + mode = ConcurrentMode | BlockingMode | StrictLegacyMode; } } else if (tag === BlockingRoot) { if (enableDoubleInvokingEffects) { - mode = BlockingMode | StrictModeL1 | StrictModeL2; + mode = BlockingMode | StrictLegacyMode | StrictEffectsMode; } else { - mode = BlockingMode | StrictModeL1; + mode = BlockingMode | StrictLegacyMode; } } else { mode = NoMode; @@ -482,8 +483,8 @@ export function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = Mode; - // TODO (StrictModeL2) Add support for new strict mode "level" attribute - mode |= StrictModeL1; + // TODO (StrictEffectsMode) Add support for new strict mode "level" attribute + mode |= StrictLegacyMode; break; case REACT_PROFILER_TYPE: return createFiberFromProfiler(pendingProps, mode, lanes, key); diff --git a/packages/react-reconciler/src/ReactFiber.old.js b/packages/react-reconciler/src/ReactFiber.old.js index 3edeb253f1b26..3f083c83c158a 100644 --- a/packages/react-reconciler/src/ReactFiber.old.js +++ b/packages/react-reconciler/src/ReactFiber.old.js @@ -65,8 +65,8 @@ import { ConcurrentMode, DebugTracingMode, ProfileMode, - StrictModeL1, - StrictModeL2, + StrictLegacyMode, + StrictEffectsMode, BlockingMode, } from './ReactTypeOfMode'; import { @@ -424,15 +424,16 @@ export function createHostRootFiber(tag: RootTag): Fiber { let mode; if (tag === ConcurrentRoot) { if (enableDoubleInvokingEffects) { - mode = ConcurrentMode | BlockingMode | StrictModeL1 | StrictModeL2; + mode = + ConcurrentMode | BlockingMode | StrictLegacyMode | StrictEffectsMode; } else { - mode = ConcurrentMode | BlockingMode | StrictModeL1; + mode = ConcurrentMode | BlockingMode | StrictLegacyMode; } } else if (tag === BlockingRoot) { if (enableDoubleInvokingEffects) { - mode = BlockingMode | StrictModeL1 | StrictModeL2; + mode = BlockingMode | StrictLegacyMode | StrictEffectsMode; } else { - mode = BlockingMode | StrictModeL1; + mode = BlockingMode | StrictLegacyMode; } } else { mode = NoMode; @@ -482,8 +483,8 @@ export function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = Mode; - // TODO (StrictModeL2) Add support for new strict mode "level" attribute - mode |= StrictModeL1; + // TODO (StrictEffectsMode) Add support for new strict mode "level" attribute + mode |= StrictLegacyMode; break; case REACT_PROFILER_TYPE: return createFiberFromProfiler(pendingProps, mode, lanes, key); diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index 3c368e562b6c0..6dffcc328927a 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -125,7 +125,7 @@ import { ConcurrentMode, NoMode, ProfileMode, - StrictModeL1, + StrictLegacyMode, BlockingMode, } from './ReactTypeOfMode'; import { @@ -357,7 +357,7 @@ function updateForwardRef( ); if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -889,7 +889,7 @@ function updateFunctionComponent( ); if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -1068,7 +1068,7 @@ function finishClassComponent( nextChildren = instance.render(); if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -1478,7 +1478,7 @@ function mountIndeterminateComponent( } } - if (workInProgress.mode & StrictModeL1) { + if (workInProgress.mode & StrictLegacyMode) { ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); } @@ -1615,7 +1615,7 @@ function mountIndeterminateComponent( if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index 112371a593f90..7801afe9fa231 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -125,7 +125,7 @@ import { ConcurrentMode, NoMode, ProfileMode, - StrictModeL1, + StrictLegacyMode, BlockingMode, } from './ReactTypeOfMode'; import { @@ -357,7 +357,7 @@ function updateForwardRef( ); if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -889,7 +889,7 @@ function updateFunctionComponent( ); if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -1068,7 +1068,7 @@ function finishClassComponent( nextChildren = instance.render(); if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -1478,7 +1478,7 @@ function mountIndeterminateComponent( } } - if (workInProgress.mode & StrictModeL1) { + if (workInProgress.mode & StrictLegacyMode) { ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); } @@ -1615,7 +1615,7 @@ function mountIndeterminateComponent( if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.new.js b/packages/react-reconciler/src/ReactFiberClassComponent.new.js index f2663d42b176e..6a4c156feb667 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.new.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.new.js @@ -33,8 +33,8 @@ import {resolveDefaultProps} from './ReactFiberLazyComponent.new'; import { DebugTracingMode, NoMode, - StrictModeL1, - StrictModeL2, + StrictLegacyMode, + StrictEffectsMode, } from './ReactTypeOfMode'; import { @@ -164,7 +164,7 @@ export function applyDerivedStateFromProps( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -317,7 +317,7 @@ function checkShouldComponentUpdate( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -654,7 +654,7 @@ function constructClassInstance( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -861,7 +861,7 @@ function mountClassInstance( } } - if (workInProgress.mode & StrictModeL1) { + if (workInProgress.mode & StrictLegacyMode) { ReactStrictModeWarnings.recordLegacyContextWarning( workInProgress, instance, @@ -909,7 +909,7 @@ function mountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & StrictModeL2) !== NoMode + (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; @@ -988,7 +988,7 @@ function resumeMountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & StrictModeL2) !== NoMode + (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; @@ -1040,7 +1040,7 @@ function resumeMountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & StrictModeL2) !== NoMode + (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; @@ -1055,7 +1055,7 @@ function resumeMountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & StrictModeL2) !== NoMode + (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.old.js b/packages/react-reconciler/src/ReactFiberClassComponent.old.js index ea8e85d7c2a88..1c81ce7c6e549 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.old.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.old.js @@ -33,8 +33,8 @@ import {resolveDefaultProps} from './ReactFiberLazyComponent.old'; import { DebugTracingMode, NoMode, - StrictModeL1, - StrictModeL2, + StrictLegacyMode, + StrictEffectsMode, } from './ReactTypeOfMode'; import { @@ -164,7 +164,7 @@ export function applyDerivedStateFromProps( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -317,7 +317,7 @@ function checkShouldComponentUpdate( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -654,7 +654,7 @@ function constructClassInstance( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -861,7 +861,7 @@ function mountClassInstance( } } - if (workInProgress.mode & StrictModeL1) { + if (workInProgress.mode & StrictLegacyMode) { ReactStrictModeWarnings.recordLegacyContextWarning( workInProgress, instance, @@ -909,7 +909,7 @@ function mountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & StrictModeL2) !== NoMode + (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; @@ -988,7 +988,7 @@ function resumeMountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & StrictModeL2) !== NoMode + (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; @@ -1040,7 +1040,7 @@ function resumeMountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & StrictModeL2) !== NoMode + (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; @@ -1055,7 +1055,7 @@ function resumeMountClassInstance( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & StrictModeL2) !== NoMode + (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. workInProgress.flags |= MountLayoutDev | Update; diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.new.js b/packages/react-reconciler/src/ReactFiberCommitWork.new.js index 85dcdda5be32d..dbf2ee7805f50 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.new.js @@ -2476,7 +2476,7 @@ function ensureCorrectReturnPointer(fiber, expectedReturnFiber) { function invokeLayoutEffectMountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check StrictModeL2 here. + // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: @@ -2510,7 +2510,7 @@ function invokeLayoutEffectMountInDEV(fiber: Fiber): void { function invokePassiveEffectMountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check StrictModeL2 here. + // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: @@ -2535,7 +2535,7 @@ function invokePassiveEffectMountInDEV(fiber: Fiber): void { function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check StrictModeL2 here. + // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: @@ -2579,7 +2579,7 @@ function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void { function invokePassiveEffectUnmountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check StrictModeL2 here. + // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.old.js b/packages/react-reconciler/src/ReactFiberCommitWork.old.js index cae91bc7cfed2..cbe7439af1de5 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.old.js @@ -2476,7 +2476,7 @@ function ensureCorrectReturnPointer(fiber, expectedReturnFiber) { function invokeLayoutEffectMountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check StrictModeL2 here. + // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: @@ -2510,7 +2510,7 @@ function invokeLayoutEffectMountInDEV(fiber: Fiber): void { function invokePassiveEffectMountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check StrictModeL2 here. + // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: @@ -2535,7 +2535,7 @@ function invokePassiveEffectMountInDEV(fiber: Fiber): void { function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check StrictModeL2 here. + // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: @@ -2579,7 +2579,7 @@ function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void { function invokePassiveEffectUnmountInDEV(fiber: Fiber): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check StrictModeL2 here. + // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { case FunctionComponent: diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 5168040e25530..be92331fa2f46 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -36,7 +36,7 @@ import { NoMode, BlockingMode, DebugTracingMode, - StrictModeL2, + StrictEffectsMode, } from './ReactTypeOfMode'; import { NoLane, @@ -510,7 +510,7 @@ export function bailoutHooks( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & StrictModeL2) !== NoMode + (workInProgress.mode & StrictEffectsMode) !== NoMode ) { workInProgress.flags &= ~( MountPassiveDevEffect | @@ -1424,7 +1424,7 @@ function mountEffect( if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & StrictModeL2) !== NoMode + (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode ) { return mountEffectImpl( MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect, @@ -1462,7 +1462,7 @@ function mountLayoutEffect( if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & StrictModeL2) !== NoMode + (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode ) { return mountEffectImpl( MountLayoutDevEffect | UpdateEffect, @@ -1534,7 +1534,7 @@ function mountImperativeHandle( if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & StrictModeL2) !== NoMode + (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode ) { return mountEffectImpl( MountLayoutDevEffect | UpdateEffect, @@ -1833,7 +1833,7 @@ function mountOpaqueIdentifier(): OpaqueIDType | void { if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & StrictModeL2) === NoMode + (currentlyRenderingFiber.mode & StrictEffectsMode) === NoMode ) { currentlyRenderingFiber.flags |= MountPassiveDevEffect | PassiveEffect; } else { diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 91375d4702243..b8b7ca2f56a57 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -36,7 +36,7 @@ import { NoMode, BlockingMode, DebugTracingMode, - StrictModeL2, + StrictEffectsMode, } from './ReactTypeOfMode'; import { NoLane, @@ -510,7 +510,7 @@ export function bailoutHooks( if ( __DEV__ && enableDoubleInvokingEffects && - (workInProgress.mode & StrictModeL2) !== NoMode + (workInProgress.mode & StrictEffectsMode) !== NoMode ) { workInProgress.flags &= ~( MountPassiveDevEffect | @@ -1424,7 +1424,7 @@ function mountEffect( if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & StrictModeL2) !== NoMode + (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode ) { return mountEffectImpl( MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect, @@ -1462,7 +1462,7 @@ function mountLayoutEffect( if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & StrictModeL2) !== NoMode + (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode ) { return mountEffectImpl( MountLayoutDevEffect | UpdateEffect, @@ -1534,7 +1534,7 @@ function mountImperativeHandle( if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & StrictModeL2) !== NoMode + (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode ) { return mountEffectImpl( MountLayoutDevEffect | UpdateEffect, @@ -1833,7 +1833,7 @@ function mountOpaqueIdentifier(): OpaqueIDType | void { if ( __DEV__ && enableDoubleInvokingEffects && - (currentlyRenderingFiber.mode & StrictModeL2) === NoMode + (currentlyRenderingFiber.mode & StrictEffectsMode) === NoMode ) { currentlyRenderingFiber.flags |= MountPassiveDevEffect | PassiveEffect; } else { diff --git a/packages/react-reconciler/src/ReactFiberReconciler.new.js b/packages/react-reconciler/src/ReactFiberReconciler.new.js index 64e8aa30f7bd8..3b4edc51e28e8 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.new.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.new.js @@ -75,7 +75,7 @@ import { resetCurrentFiber as resetCurrentDebugFiberInDEV, setCurrentFiber as setCurrentDebugFiberInDEV, } from './ReactCurrentFiber'; -import {StrictModeL1} from './ReactTypeOfMode'; +import {StrictLegacyMode} from './ReactTypeOfMode'; import { SyncLane, InputDiscreteHydrationLane, @@ -204,7 +204,7 @@ function findHostInstanceWithWarning( if (hostFiber === null) { return null; } - if (hostFiber.mode & StrictModeL1) { + if (hostFiber.mode & StrictLegacyMode) { const componentName = getComponentName(fiber.type) || 'Component'; if (!didWarnAboutFindNodeInStrictMode[componentName]) { didWarnAboutFindNodeInStrictMode[componentName] = true; @@ -212,7 +212,7 @@ function findHostInstanceWithWarning( const previousFiber = ReactCurrentFiberCurrent; try { setCurrentDebugFiberInDEV(hostFiber); - if (fiber.mode & StrictModeL1) { + if (fiber.mode & StrictLegacyMode) { console.error( '%s is deprecated in StrictMode. ' + '%s was passed an instance of %s which is inside StrictMode. ' + diff --git a/packages/react-reconciler/src/ReactFiberReconciler.old.js b/packages/react-reconciler/src/ReactFiberReconciler.old.js index f4207586aab5e..ecbcbd8a9e5e3 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.old.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.old.js @@ -75,7 +75,7 @@ import { resetCurrentFiber as resetCurrentDebugFiberInDEV, setCurrentFiber as setCurrentDebugFiberInDEV, } from './ReactCurrentFiber'; -import {StrictModeL1} from './ReactTypeOfMode'; +import {StrictLegacyMode} from './ReactTypeOfMode'; import { SyncLane, InputDiscreteHydrationLane, @@ -204,7 +204,7 @@ function findHostInstanceWithWarning( if (hostFiber === null) { return null; } - if (hostFiber.mode & StrictModeL1) { + if (hostFiber.mode & StrictLegacyMode) { const componentName = getComponentName(fiber.type) || 'Component'; if (!didWarnAboutFindNodeInStrictMode[componentName]) { didWarnAboutFindNodeInStrictMode[componentName] = true; @@ -212,7 +212,7 @@ function findHostInstanceWithWarning( const previousFiber = ReactCurrentFiberCurrent; try { setCurrentDebugFiberInDEV(hostFiber); - if (fiber.mode & StrictModeL1) { + if (fiber.mode & StrictLegacyMode) { console.error( '%s is deprecated in StrictMode. ' + '%s was passed an instance of %s which is inside StrictMode. ' + diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 68558b6160cc5..eb6c139486b38 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -104,8 +104,8 @@ import { } from './ReactFiber.new'; import { NoMode, - StrictModeL1, - StrictModeL2, + StrictLegacyMode, + StrictEffectsMode, ProfileMode, BlockingMode, ConcurrentMode, @@ -2562,8 +2562,8 @@ function commitDoubleInvokeEffectsInDEV( hasPassiveEffects: boolean, ) { if (__DEV__ && enableDoubleInvokingEffects) { - // Never double-invoke effects outside of StrictModeL2. - if ((fiber.mode & StrictModeL2) === NoMode) { + // Never double-invoke effects outside of StrictEffectsMode. + if ((fiber.mode & StrictEffectsMode) === NoMode) { return; } @@ -2591,7 +2591,7 @@ function invokeEffectsInDev( invokeEffectFn: (fiber: Fiber) => void, ): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check StrictModeL2 here. + // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. let current = firstChild; @@ -2935,7 +2935,7 @@ export function warnIfNotCurrentlyActingEffectsInDEV(fiber: Fiber): void { if (__DEV__) { if ( warnsIfNotActing === true && - (fiber.mode & StrictModeL1) !== NoMode && + (fiber.mode & StrictLegacyMode) !== NoMode && IsSomeRendererActing.current === false && IsThisRendererActing.current === false ) { diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 35906c1412b8b..049d9387ba4ba 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -104,8 +104,8 @@ import { } from './ReactFiber.old'; import { NoMode, - StrictModeL1, - StrictModeL2, + StrictLegacyMode, + StrictEffectsMode, ProfileMode, BlockingMode, ConcurrentMode, @@ -2562,8 +2562,8 @@ function commitDoubleInvokeEffectsInDEV( hasPassiveEffects: boolean, ) { if (__DEV__ && enableDoubleInvokingEffects) { - // Never double-invoke effects outside of StrictModeL2. - if ((fiber.mode & StrictModeL2) === NoMode) { + // Never double-invoke effects outside of StrictEffectsMode. + if ((fiber.mode & StrictEffectsMode) === NoMode) { return; } @@ -2591,7 +2591,7 @@ function invokeEffectsInDev( invokeEffectFn: (fiber: Fiber) => void, ): void { if (__DEV__ && enableDoubleInvokingEffects) { - // We don't need to re-check StrictModeL2 here. + // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. let current = firstChild; @@ -2935,7 +2935,7 @@ export function warnIfNotCurrentlyActingEffectsInDEV(fiber: Fiber): void { if (__DEV__) { if ( warnsIfNotActing === true && - (fiber.mode & StrictModeL1) !== NoMode && + (fiber.mode & StrictLegacyMode) !== NoMode && IsSomeRendererActing.current === false && IsThisRendererActing.current === false ) { diff --git a/packages/react-reconciler/src/ReactStrictModeWarnings.new.js b/packages/react-reconciler/src/ReactStrictModeWarnings.new.js index 865179a0792d2..972c106c632cf 100644 --- a/packages/react-reconciler/src/ReactStrictModeWarnings.new.js +++ b/packages/react-reconciler/src/ReactStrictModeWarnings.new.js @@ -14,7 +14,7 @@ import { setCurrentFiber as setCurrentDebugFiberInDEV, } from './ReactCurrentFiber'; import getComponentName from 'shared/getComponentName'; -import {StrictModeL1} from './ReactTypeOfMode'; +import {StrictLegacyMode} from './ReactTypeOfMode'; type FiberArray = Array; type FiberToFiberComponentsMap = Map; @@ -33,7 +33,7 @@ if (__DEV__) { let node = fiber; while (node !== null) { - if (node.mode & StrictModeL1) { + if (node.mode & StrictLegacyMode) { maybeStrictRoot = node; } node = node.return; @@ -78,7 +78,7 @@ if (__DEV__) { } if ( - fiber.mode & StrictModeL1 && + fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillMount === 'function' ) { pendingUNSAFE_ComponentWillMountWarnings.push(fiber); @@ -92,7 +92,7 @@ if (__DEV__) { } if ( - fiber.mode & StrictModeL1 && + fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillReceiveProps === 'function' ) { pendingUNSAFE_ComponentWillReceivePropsWarnings.push(fiber); @@ -106,7 +106,7 @@ if (__DEV__) { } if ( - fiber.mode & StrictModeL1 && + fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillUpdate === 'function' ) { pendingUNSAFE_ComponentWillUpdateWarnings.push(fiber); diff --git a/packages/react-reconciler/src/ReactStrictModeWarnings.old.js b/packages/react-reconciler/src/ReactStrictModeWarnings.old.js index 865179a0792d2..972c106c632cf 100644 --- a/packages/react-reconciler/src/ReactStrictModeWarnings.old.js +++ b/packages/react-reconciler/src/ReactStrictModeWarnings.old.js @@ -14,7 +14,7 @@ import { setCurrentFiber as setCurrentDebugFiberInDEV, } from './ReactCurrentFiber'; import getComponentName from 'shared/getComponentName'; -import {StrictModeL1} from './ReactTypeOfMode'; +import {StrictLegacyMode} from './ReactTypeOfMode'; type FiberArray = Array; type FiberToFiberComponentsMap = Map; @@ -33,7 +33,7 @@ if (__DEV__) { let node = fiber; while (node !== null) { - if (node.mode & StrictModeL1) { + if (node.mode & StrictLegacyMode) { maybeStrictRoot = node; } node = node.return; @@ -78,7 +78,7 @@ if (__DEV__) { } if ( - fiber.mode & StrictModeL1 && + fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillMount === 'function' ) { pendingUNSAFE_ComponentWillMountWarnings.push(fiber); @@ -92,7 +92,7 @@ if (__DEV__) { } if ( - fiber.mode & StrictModeL1 && + fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillReceiveProps === 'function' ) { pendingUNSAFE_ComponentWillReceivePropsWarnings.push(fiber); @@ -106,7 +106,7 @@ if (__DEV__) { } if ( - fiber.mode & StrictModeL1 && + fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillUpdate === 'function' ) { pendingUNSAFE_ComponentWillUpdateWarnings.push(fiber); diff --git a/packages/react-reconciler/src/ReactTypeOfMode.js b/packages/react-reconciler/src/ReactTypeOfMode.js index 7025d3d1f5877..a6499be7aca11 100644 --- a/packages/react-reconciler/src/ReactTypeOfMode.js +++ b/packages/react-reconciler/src/ReactTypeOfMode.js @@ -9,11 +9,11 @@ export type TypeOfMode = number; -export const NoMode = /* */ 0b000000; +export const NoMode = /* */ 0b000000; // TODO: Remove BlockingMode and ConcurrentMode by reading from the root tag instead -export const BlockingMode = /* */ 0b000001; -export const ConcurrentMode = /* */ 0b000010; -export const ProfileMode = /* */ 0b000100; -export const DebugTracingMode = /* */ 0b001000; -export const StrictModeL1 = /* */ 0b010000; -export const StrictModeL2 = /* */ 0b100000; +export const BlockingMode = /* */ 0b000001; +export const ConcurrentMode = /* */ 0b000010; +export const ProfileMode = /* */ 0b000100; +export const DebugTracingMode = /* */ 0b001000; +export const StrictLegacyMode = /* */ 0b010000; +export const StrictEffectsMode = /* */ 0b100000; diff --git a/packages/react-reconciler/src/ReactUpdateQueue.new.js b/packages/react-reconciler/src/ReactUpdateQueue.new.js index 46c9e6026f130..0bdcbf580764a 100644 --- a/packages/react-reconciler/src/ReactUpdateQueue.new.js +++ b/packages/react-reconciler/src/ReactUpdateQueue.new.js @@ -104,7 +104,7 @@ import {Callback, ShouldCapture, DidCapture} from './ReactFiberFlags'; import {debugRenderPhaseSideEffectsForStrictMode} from 'shared/ReactFeatureFlags'; -import {StrictModeL1} from './ReactTypeOfMode'; +import {StrictLegacyMode} from './ReactTypeOfMode'; import { markSkippedUpdateLanes, isInterleavedUpdate, @@ -392,7 +392,7 @@ function getStateFromUpdate( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -425,7 +425,7 @@ function getStateFromUpdate( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { diff --git a/packages/react-reconciler/src/ReactUpdateQueue.old.js b/packages/react-reconciler/src/ReactUpdateQueue.old.js index 8b4a6b2bff67b..209abfb32e743 100644 --- a/packages/react-reconciler/src/ReactUpdateQueue.old.js +++ b/packages/react-reconciler/src/ReactUpdateQueue.old.js @@ -104,7 +104,7 @@ import {Callback, ShouldCapture, DidCapture} from './ReactFiberFlags'; import {debugRenderPhaseSideEffectsForStrictMode} from 'shared/ReactFeatureFlags'; -import {StrictModeL1} from './ReactTypeOfMode'; +import {StrictLegacyMode} from './ReactTypeOfMode'; import { markSkippedUpdateLanes, isInterleavedUpdate, @@ -392,7 +392,7 @@ function getStateFromUpdate( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { @@ -425,7 +425,7 @@ function getStateFromUpdate( if (__DEV__) { if ( debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictModeL1 + workInProgress.mode & StrictLegacyMode ) { disableLogs(); try { diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 331989800783b..43381ed8b8b06 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -20,11 +20,11 @@ export const enableDebugTracing = false; export const enableSchedulingProfiler = __PROFILE__ && __EXPERIMENTAL__; // Helps identify side effects in render-phase lifecycle hooks and setState -// reducers by double invoking them in StrictModeL1. +// reducers by double invoking them in StrictLegacyMode. export const debugRenderPhaseSideEffectsForStrictMode = __DEV__; // Helps identify code that is not safe for planned Offscreen API and Suspense semantics; -// this feature flag only impacts StrictModeL2. +// this feature flag only impacts StrictEffectsMode. export const enableDoubleInvokingEffects = false; // To preserve the "Pause on caught exceptions" behavior of the debugger, we From 4d24e7b05363dc6640c9cbfd51b01b40275e2771 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 24 Feb 2021 15:02:22 -0500 Subject: [PATCH 3/5] Renamed tests --- ...{ReactDoubleInvokeEvents-test.js => StrictEffectsMode-test.js} | 0 ...est.internal.js => StrictEffectsModeDefaults-test.internal.js} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename packages/react-reconciler/src/__tests__/{ReactDoubleInvokeEvents-test.js => StrictEffectsMode-test.js} (100%) rename packages/react-reconciler/src/__tests__/{ReactDoubleInvokeEvents-test.internal.js => StrictEffectsModeDefaults-test.internal.js} (100%) diff --git a/packages/react-reconciler/src/__tests__/ReactDoubleInvokeEvents-test.js b/packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js similarity index 100% rename from packages/react-reconciler/src/__tests__/ReactDoubleInvokeEvents-test.js rename to packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js diff --git a/packages/react-reconciler/src/__tests__/ReactDoubleInvokeEvents-test.internal.js b/packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js similarity index 100% rename from packages/react-reconciler/src/__tests__/ReactDoubleInvokeEvents-test.internal.js rename to packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js From a1d647535864b4a710f96136b5203c8cdf4731bc Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 24 Feb 2021 15:06:05 -0500 Subject: [PATCH 4/5] Split strict effects mode into two flags One flag ('enableStrictEffects') enables strict mode level 2. It is similar to 'debugRenderPhaseSideEffectsForStrictMode' which enables srtict mode level 1. The second flag ('createRootStrictEffectsByDefault') controls the default strict mode level for 'createRoot' trees. For now, all 'createRoot' trees remain level 1 by default. We will experiment with level 2 within Facebook. This is a prerequisite for adding a configurable option to 'createRoot' that enables choosing a different StrictMode level than the default. --- .../react-reconciler/src/ReactFiber.new.js | 7 +- .../react-reconciler/src/ReactFiber.old.js | 7 +- .../src/ReactFiberClassComponent.new.js | 10 +- .../src/ReactFiberClassComponent.old.js | 10 +- .../src/ReactFiberCommitWork.new.js | 10 +- .../src/ReactFiberCommitWork.old.js | 10 +- .../src/ReactFiberHooks.new.js | 12 +- .../src/ReactFiberHooks.old.js | 12 +- .../src/ReactFiberWorkLoop.new.js | 10 +- .../src/ReactFiberWorkLoop.old.js | 10 +- .../src/__tests__/StrictEffectsMode-test.js | 5 +- ...StrictEffectsModeDefaults-test.internal.js | 774 ++++++++---------- .../__tests__/ReactProfiler-test.internal.js | 3 +- packages/shared/ReactFeatureFlags.js | 6 +- .../forks/ReactFeatureFlags.native-fb.js | 3 +- .../forks/ReactFeatureFlags.native-oss.js | 3 +- .../forks/ReactFeatureFlags.test-renderer.js | 3 +- .../ReactFeatureFlags.test-renderer.native.js | 3 +- .../ReactFeatureFlags.test-renderer.www.js | 3 +- .../shared/forks/ReactFeatureFlags.testing.js | 3 +- .../forks/ReactFeatureFlags.testing.www.js | 3 +- .../forks/ReactFeatureFlags.www-dynamic.js | 3 +- .../shared/forks/ReactFeatureFlags.www.js | 3 +- 23 files changed, 409 insertions(+), 504 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiber.new.js b/packages/react-reconciler/src/ReactFiber.new.js index 69f1ab744498c..d1a4837fdba01 100644 --- a/packages/react-reconciler/src/ReactFiber.new.js +++ b/packages/react-reconciler/src/ReactFiber.new.js @@ -19,8 +19,9 @@ import type {OffscreenProps} from './ReactFiberOffscreenComponent'; import invariant from 'shared/invariant'; import { + createRootStrictEffectsByDefault, enableCache, - enableDoubleInvokingEffects, + enableStrictEffects, enableProfilerTimer, enableScopeAPI, } from 'shared/ReactFeatureFlags'; @@ -423,14 +424,14 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) { export function createHostRootFiber(tag: RootTag): Fiber { let mode; if (tag === ConcurrentRoot) { - if (enableDoubleInvokingEffects) { + if (enableStrictEffects && createRootStrictEffectsByDefault) { mode = ConcurrentMode | BlockingMode | StrictLegacyMode | StrictEffectsMode; } else { mode = ConcurrentMode | BlockingMode | StrictLegacyMode; } } else if (tag === BlockingRoot) { - if (enableDoubleInvokingEffects) { + if (enableStrictEffects && createRootStrictEffectsByDefault) { mode = BlockingMode | StrictLegacyMode | StrictEffectsMode; } else { mode = BlockingMode | StrictLegacyMode; diff --git a/packages/react-reconciler/src/ReactFiber.old.js b/packages/react-reconciler/src/ReactFiber.old.js index 3f083c83c158a..6f79f0331d348 100644 --- a/packages/react-reconciler/src/ReactFiber.old.js +++ b/packages/react-reconciler/src/ReactFiber.old.js @@ -19,8 +19,9 @@ import type {OffscreenProps} from './ReactFiberOffscreenComponent'; import invariant from 'shared/invariant'; import { + createRootStrictEffectsByDefault, enableCache, - enableDoubleInvokingEffects, + enableStrictEffects, enableProfilerTimer, enableScopeAPI, } from 'shared/ReactFeatureFlags'; @@ -423,14 +424,14 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) { export function createHostRootFiber(tag: RootTag): Fiber { let mode; if (tag === ConcurrentRoot) { - if (enableDoubleInvokingEffects) { + if (enableStrictEffects && createRootStrictEffectsByDefault) { mode = ConcurrentMode | BlockingMode | StrictLegacyMode | StrictEffectsMode; } else { mode = ConcurrentMode | BlockingMode | StrictLegacyMode; } } else if (tag === BlockingRoot) { - if (enableDoubleInvokingEffects) { + if (enableStrictEffects && createRootStrictEffectsByDefault) { mode = BlockingMode | StrictLegacyMode | StrictEffectsMode; } else { mode = BlockingMode | StrictLegacyMode; diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.new.js b/packages/react-reconciler/src/ReactFiberClassComponent.new.js index 6a4c156feb667..8259e59a70bdd 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.new.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.new.js @@ -19,7 +19,7 @@ import { enableDebugTracing, enableSchedulingProfiler, warnAboutDeprecatedLifecycles, - enableDoubleInvokingEffects, + enableStrictEffects, } from 'shared/ReactFeatureFlags'; import ReactStrictModeWarnings from './ReactStrictModeWarnings.new'; import {isMounted} from './ReactFiberTreeReflection'; @@ -908,7 +908,7 @@ function mountClassInstance( if (typeof instance.componentDidMount === 'function') { if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. @@ -987,7 +987,7 @@ function resumeMountClassInstance( if (typeof instance.componentDidMount === 'function') { if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. @@ -1039,7 +1039,7 @@ function resumeMountClassInstance( if (typeof instance.componentDidMount === 'function') { if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. @@ -1054,7 +1054,7 @@ function resumeMountClassInstance( if (typeof instance.componentDidMount === 'function') { if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.old.js b/packages/react-reconciler/src/ReactFiberClassComponent.old.js index 1c81ce7c6e549..fe20322bb15a6 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.old.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.old.js @@ -19,7 +19,7 @@ import { enableDebugTracing, enableSchedulingProfiler, warnAboutDeprecatedLifecycles, - enableDoubleInvokingEffects, + enableStrictEffects, } from 'shared/ReactFeatureFlags'; import ReactStrictModeWarnings from './ReactStrictModeWarnings.old'; import {isMounted} from './ReactFiberTreeReflection'; @@ -908,7 +908,7 @@ function mountClassInstance( if (typeof instance.componentDidMount === 'function') { if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. @@ -987,7 +987,7 @@ function resumeMountClassInstance( if (typeof instance.componentDidMount === 'function') { if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. @@ -1039,7 +1039,7 @@ function resumeMountClassInstance( if (typeof instance.componentDidMount === 'function') { if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. @@ -1054,7 +1054,7 @@ function resumeMountClassInstance( if (typeof instance.componentDidMount === 'function') { if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (workInProgress.mode & StrictEffectsMode) !== NoMode ) { // Never double-invoke effects for legacy roots. diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.new.js b/packages/react-reconciler/src/ReactFiberCommitWork.new.js index dbf2ee7805f50..33ed1b5c95ce7 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.new.js @@ -35,7 +35,7 @@ import { enableSuspenseServerRenderer, enableSuspenseCallback, enableScopeAPI, - enableDoubleInvokingEffects, + enableStrictEffects, } from 'shared/ReactFeatureFlags'; import { FunctionComponent, @@ -2475,7 +2475,7 @@ function ensureCorrectReturnPointer(fiber, expectedReturnFiber) { } function invokeLayoutEffectMountInDEV(fiber: Fiber): void { - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { @@ -2509,7 +2509,7 @@ function invokeLayoutEffectMountInDEV(fiber: Fiber): void { } function invokePassiveEffectMountInDEV(fiber: Fiber): void { - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { @@ -2534,7 +2534,7 @@ function invokePassiveEffectMountInDEV(fiber: Fiber): void { } function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void { - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { @@ -2578,7 +2578,7 @@ function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void { } function invokePassiveEffectUnmountInDEV(fiber: Fiber): void { - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.old.js b/packages/react-reconciler/src/ReactFiberCommitWork.old.js index cbe7439af1de5..7574eb4fd3d3c 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.old.js @@ -35,7 +35,7 @@ import { enableSuspenseServerRenderer, enableSuspenseCallback, enableScopeAPI, - enableDoubleInvokingEffects, + enableStrictEffects, } from 'shared/ReactFeatureFlags'; import { FunctionComponent, @@ -2475,7 +2475,7 @@ function ensureCorrectReturnPointer(fiber, expectedReturnFiber) { } function invokeLayoutEffectMountInDEV(fiber: Fiber): void { - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { @@ -2509,7 +2509,7 @@ function invokeLayoutEffectMountInDEV(fiber: Fiber): void { } function invokePassiveEffectMountInDEV(fiber: Fiber): void { - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { @@ -2534,7 +2534,7 @@ function invokePassiveEffectMountInDEV(fiber: Fiber): void { } function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void { - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { @@ -2578,7 +2578,7 @@ function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void { } function invokePassiveEffectUnmountInDEV(fiber: Fiber): void { - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. switch (fiber.tag) { diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index be92331fa2f46..9d1fa5f82ed8e 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -29,7 +29,7 @@ import { enableCache, decoupleUpdatePriorityFromScheduler, enableUseRefAccessWarning, - enableDoubleInvokingEffects, + enableStrictEffects, } from 'shared/ReactFeatureFlags'; import { @@ -509,7 +509,7 @@ export function bailoutHooks( // complete phase (bubbleProperties). if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (workInProgress.mode & StrictEffectsMode) !== NoMode ) { workInProgress.flags &= ~( @@ -1423,7 +1423,7 @@ function mountEffect( } if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode ) { return mountEffectImpl( @@ -1461,7 +1461,7 @@ function mountLayoutEffect( ): void { if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode ) { return mountEffectImpl( @@ -1533,7 +1533,7 @@ function mountImperativeHandle( if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode ) { return mountEffectImpl( @@ -1832,7 +1832,7 @@ function mountOpaqueIdentifier(): OpaqueIDType | void { if ((currentlyRenderingFiber.mode & BlockingMode) === NoMode) { if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (currentlyRenderingFiber.mode & StrictEffectsMode) === NoMode ) { currentlyRenderingFiber.flags |= MountPassiveDevEffect | PassiveEffect; diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index b8b7ca2f56a57..61f1a17452e4b 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -29,7 +29,7 @@ import { enableCache, decoupleUpdatePriorityFromScheduler, enableUseRefAccessWarning, - enableDoubleInvokingEffects, + enableStrictEffects, } from 'shared/ReactFeatureFlags'; import { @@ -509,7 +509,7 @@ export function bailoutHooks( // complete phase (bubbleProperties). if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (workInProgress.mode & StrictEffectsMode) !== NoMode ) { workInProgress.flags &= ~( @@ -1423,7 +1423,7 @@ function mountEffect( } if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode ) { return mountEffectImpl( @@ -1461,7 +1461,7 @@ function mountLayoutEffect( ): void { if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode ) { return mountEffectImpl( @@ -1533,7 +1533,7 @@ function mountImperativeHandle( if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode ) { return mountEffectImpl( @@ -1832,7 +1832,7 @@ function mountOpaqueIdentifier(): OpaqueIDType | void { if ((currentlyRenderingFiber.mode & BlockingMode) === NoMode) { if ( __DEV__ && - enableDoubleInvokingEffects && + enableStrictEffects && (currentlyRenderingFiber.mode & StrictEffectsMode) === NoMode ) { currentlyRenderingFiber.flags |= MountPassiveDevEffect | PassiveEffect; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index eb6c139486b38..ebc9fb8f17605 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -32,7 +32,7 @@ import { enableDebugTracing, enableSchedulingProfiler, disableSchedulerTimeoutInWorkLoop, - enableDoubleInvokingEffects, + enableStrictEffects, skipUnmountedBoundaries, enableNativeEventPriorityInference, } from 'shared/ReactFeatureFlags'; @@ -2071,7 +2071,7 @@ function commitRootImpl(root, renderPriorityLevel) { legacyErrorBoundariesThatAlreadyFailed = null; } - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { if (!rootDidHavePassiveEffects) { commitDoubleInvokeEffectsInDEV(root.current, false); } @@ -2258,7 +2258,7 @@ function flushPassiveEffectsImpl() { markPassiveEffectsStopped(); } - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { commitDoubleInvokeEffectsInDEV(root.current, true); } @@ -2561,7 +2561,7 @@ function commitDoubleInvokeEffectsInDEV( fiber: Fiber, hasPassiveEffects: boolean, ) { - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { // Never double-invoke effects outside of StrictEffectsMode. if ((fiber.mode & StrictEffectsMode) === NoMode) { return; @@ -2590,7 +2590,7 @@ function invokeEffectsInDev( fiberFlags: Flags, invokeEffectFn: (fiber: Fiber) => void, ): void { - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 049d9387ba4ba..25b77701d8699 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -32,7 +32,7 @@ import { enableDebugTracing, enableSchedulingProfiler, disableSchedulerTimeoutInWorkLoop, - enableDoubleInvokingEffects, + enableStrictEffects, skipUnmountedBoundaries, enableNativeEventPriorityInference, } from 'shared/ReactFeatureFlags'; @@ -2071,7 +2071,7 @@ function commitRootImpl(root, renderPriorityLevel) { legacyErrorBoundariesThatAlreadyFailed = null; } - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { if (!rootDidHavePassiveEffects) { commitDoubleInvokeEffectsInDEV(root.current, false); } @@ -2258,7 +2258,7 @@ function flushPassiveEffectsImpl() { markPassiveEffectsStopped(); } - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { commitDoubleInvokeEffectsInDEV(root.current, true); } @@ -2561,7 +2561,7 @@ function commitDoubleInvokeEffectsInDEV( fiber: Fiber, hasPassiveEffects: boolean, ) { - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { // Never double-invoke effects outside of StrictEffectsMode. if ((fiber.mode & StrictEffectsMode) === NoMode) { return; @@ -2590,7 +2590,7 @@ function invokeEffectsInDev( fiberFlags: Flags, invokeEffectFn: (fiber: Fiber) => void, ): void { - if (__DEV__ && enableDoubleInvokingEffects) { + if (__DEV__ && enableStrictEffects) { // We don't need to re-check StrictEffectsMode here. // This function is only called if that check has already passed. diff --git a/packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js b/packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js index 5b6abbc4f6593..13f7a9ac543c2 100644 --- a/packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js +++ b/packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js @@ -14,7 +14,7 @@ let ReactTestRenderer; let Scheduler; let act; -describe('ReactDoubleInvokeEvents', () => { +describe('StrictEffectsMode', () => { beforeEach(() => { jest.resetModules(); React = require('react'); @@ -27,7 +27,8 @@ describe('ReactDoubleInvokeEvents', () => { return gate( flags => flags.build === 'development' && - flags.enableDoubleInvokingEffects && + flags.enableStrictEffects && + flags.createRootStrictEffectsByDefault && flags.dfsEffectsRefactor, ); } diff --git a/packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js b/packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js index b53b7b719c6ba..469f43348beaf 100644 --- a/packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js +++ b/packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js @@ -10,25 +10,20 @@ 'use strict'; let React; -let ReactFeatureFlags; let ReactNoop; let Scheduler; -function shouldDoubleInvokingEffects() { - // For now, this feature only exists in the old fork (while the new fork is being bisected). - // Eventually we'll land it in both forks. - return __DEV__; -} - -describe('ReactDoubleInvokeEvents', () => { +describe('StrictEffectsMode defaults', () => { beforeEach(() => { jest.resetModules(); + React = require('react'); - ReactFeatureFlags = require('shared/ReactFeatureFlags'); ReactNoop = require('react-noop-renderer'); Scheduler = require('scheduler'); - ReactFeatureFlags.enableDoubleInvokingEffects = shouldDoubleInvokingEffects(); + const ReactFeatureFlags = require('shared/ReactFeatureFlags'); + ReactFeatureFlags.enableStrictEffects = __DEV__; + ReactFeatureFlags.createRootStrictEffectsByDefault = __DEV__; }); it('should not double invoke effects in legacy mode', () => { @@ -82,46 +77,40 @@ describe('ReactDoubleInvokeEvents', () => { expect(Scheduler).toHaveYielded(['componentDidMount']); }); - it('should flush double-invoked effects within the same frame as layout effects if there are no passive effects', () => { - function ComponentWithEffects({label}) { - React.useLayoutEffect(() => { - Scheduler.unstable_yieldValue(`useLayoutEffect mount "${label}"`); - return () => - Scheduler.unstable_yieldValue(`useLayoutEffect unmount "${label}"`); - }); + if (__DEV__) { + it('should flush double-invoked effects within the same frame as layout effects if there are no passive effects', () => { + function ComponentWithEffects({label}) { + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue(`useLayoutEffect mount "${label}"`); + return () => + Scheduler.unstable_yieldValue(`useLayoutEffect unmount "${label}"`); + }); - return label; - } + return label; + } - ReactNoop.act(() => { - ReactNoop.render( - <> - - , - ); + ReactNoop.act(() => { + ReactNoop.render( + <> + + , + ); - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toFlushUntilNextPaint([ 'useLayoutEffect mount "one"', 'useLayoutEffect unmount "one"', 'useLayoutEffect mount "one"', ]); - } else { - expect(Scheduler).toFlushUntilNextPaint([ - 'useLayoutEffect mount "one"', - ]); - } - }); + }); + + ReactNoop.act(() => { + ReactNoop.render( + <> + + + , + ); - ReactNoop.act(() => { - ReactNoop.render( - <> - - - , - ); - - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toFlushUntilNextPaint([ // Cleanup and re-run "one" (and "two") since there is no dependencies array. 'useLayoutEffect unmount "one"', @@ -132,43 +121,35 @@ describe('ReactDoubleInvokeEvents', () => { 'useLayoutEffect unmount "two"', 'useLayoutEffect mount "two"', ]); - } else { - expect(Scheduler).toFlushUntilNextPaint([ - 'useLayoutEffect unmount "one"', - 'useLayoutEffect mount "one"', - 'useLayoutEffect mount "two"', - ]); - } - }); - }); - - // This test also verifies that double-invoked effects flush synchronously - // within the same frame as passive effects. - it('should double invoke effects only for newly mounted components', () => { - function ComponentWithEffects({label}) { - React.useEffect(() => { - Scheduler.unstable_yieldValue(`useEffect mount "${label}"`); - return () => - Scheduler.unstable_yieldValue(`useEffect unmount "${label}"`); - }); - - React.useLayoutEffect(() => { - Scheduler.unstable_yieldValue(`useLayoutEffect mount "${label}"`); - return () => - Scheduler.unstable_yieldValue(`useLayoutEffect unmount "${label}"`); }); + }); - return label; - } + // This test also verifies that double-invoked effects flush synchronously + // within the same frame as passive effects. + it('should double invoke effects only for newly mounted components', () => { + function ComponentWithEffects({label}) { + React.useEffect(() => { + Scheduler.unstable_yieldValue(`useEffect mount "${label}"`); + return () => + Scheduler.unstable_yieldValue(`useEffect unmount "${label}"`); + }); + + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue(`useLayoutEffect mount "${label}"`); + return () => + Scheduler.unstable_yieldValue(`useLayoutEffect unmount "${label}"`); + }); + + return label; + } - ReactNoop.act(() => { - ReactNoop.render( - <> - - , - ); + ReactNoop.act(() => { + ReactNoop.render( + <> + + , + ); - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toFlushAndYieldThrough([ 'useLayoutEffect mount "one"', ]); @@ -179,23 +160,16 @@ describe('ReactDoubleInvokeEvents', () => { 'useLayoutEffect mount "one"', 'useEffect mount "one"', ]); - } else { - expect(Scheduler).toFlushAndYieldThrough([ - 'useLayoutEffect mount "one"', - ]); - expect(Scheduler).toFlushAndYield(['useEffect mount "one"']); - } - }); + }); + + ReactNoop.act(() => { + ReactNoop.render( + <> + + + , + ); - ReactNoop.act(() => { - ReactNoop.render( - <> - - - , - ); - - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toFlushAndYieldThrough([ // Cleanup and re-run "one" (and "two") since there is no dependencies array. 'useLayoutEffect unmount "one"', @@ -213,40 +187,27 @@ describe('ReactDoubleInvokeEvents', () => { 'useLayoutEffect mount "two"', 'useEffect mount "two"', ]); - } else { - expect(Scheduler).toFlushAndYieldThrough([ - 'useLayoutEffect unmount "one"', - 'useLayoutEffect mount "one"', - 'useLayoutEffect mount "two"', - ]); - expect(Scheduler).toFlushAndYield([ - 'useEffect unmount "one"', - 'useEffect mount "one"', - 'useEffect mount "two"', - ]); - } + }); }); - }); - it('double invoking for effects for modern roots', () => { - function App({text}) { - React.useEffect(() => { - Scheduler.unstable_yieldValue('useEffect mount'); - return () => Scheduler.unstable_yieldValue('useEffect unmount'); - }); + it('double invoking for effects for modern roots', () => { + function App({text}) { + React.useEffect(() => { + Scheduler.unstable_yieldValue('useEffect mount'); + return () => Scheduler.unstable_yieldValue('useEffect unmount'); + }); - React.useLayoutEffect(() => { - Scheduler.unstable_yieldValue('useLayoutEffect mount'); - return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount'); - }); + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue('useLayoutEffect mount'); + return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount'); + }); - return text; - } - ReactNoop.act(() => { - ReactNoop.render(); - }); + return text; + } + ReactNoop.act(() => { + ReactNoop.render(); + }); - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toHaveYielded([ 'useLayoutEffect mount', 'useEffect mount', @@ -255,54 +216,47 @@ describe('ReactDoubleInvokeEvents', () => { 'useLayoutEffect mount', 'useEffect mount', ]); - } else { + + ReactNoop.act(() => { + ReactNoop.render(); + }); + expect(Scheduler).toHaveYielded([ + 'useLayoutEffect unmount', 'useLayoutEffect mount', + 'useEffect unmount', 'useEffect mount', ]); - } - - ReactNoop.act(() => { - ReactNoop.render(); - }); - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect unmount', - 'useLayoutEffect mount', - 'useEffect unmount', - 'useEffect mount', - ]); + ReactNoop.act(() => { + ReactNoop.render(null); + }); - ReactNoop.act(() => { - ReactNoop.render(null); + expect(Scheduler).toHaveYielded([ + 'useLayoutEffect unmount', + 'useEffect unmount', + ]); }); - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect unmount', - 'useEffect unmount', - ]); - }); - - it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => { - function App({text}) { - React.useEffect(() => { - Scheduler.unstable_yieldValue('useEffect One mount'); - return () => Scheduler.unstable_yieldValue('useEffect One unmount'); - }); + it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => { + function App({text}) { + React.useEffect(() => { + Scheduler.unstable_yieldValue('useEffect One mount'); + return () => Scheduler.unstable_yieldValue('useEffect One unmount'); + }); - React.useEffect(() => { - Scheduler.unstable_yieldValue('useEffect Two mount'); - return () => Scheduler.unstable_yieldValue('useEffect Two unmount'); - }); + React.useEffect(() => { + Scheduler.unstable_yieldValue('useEffect Two mount'); + return () => Scheduler.unstable_yieldValue('useEffect Two unmount'); + }); - return text; - } + return text; + } - ReactNoop.act(() => { - ReactNoop.render(); - }); + ReactNoop.act(() => { + ReactNoop.render(); + }); - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toHaveYielded([ 'useEffect One mount', 'useEffect Two mount', @@ -311,56 +265,49 @@ describe('ReactDoubleInvokeEvents', () => { 'useEffect One mount', 'useEffect Two mount', ]); - } else { + + ReactNoop.act(() => { + ReactNoop.render(); + }); + expect(Scheduler).toHaveYielded([ + 'useEffect One unmount', + 'useEffect Two unmount', 'useEffect One mount', 'useEffect Two mount', ]); - } - - ReactNoop.act(() => { - ReactNoop.render(); - }); - expect(Scheduler).toHaveYielded([ - 'useEffect One unmount', - 'useEffect Two unmount', - 'useEffect One mount', - 'useEffect Two mount', - ]); + ReactNoop.act(() => { + ReactNoop.render(null); + }); - ReactNoop.act(() => { - ReactNoop.render(null); + expect(Scheduler).toHaveYielded([ + 'useEffect One unmount', + 'useEffect Two unmount', + ]); }); - expect(Scheduler).toHaveYielded([ - 'useEffect One unmount', - 'useEffect Two unmount', - ]); - }); - - it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => { - function App({text}) { - React.useLayoutEffect(() => { - Scheduler.unstable_yieldValue('useLayoutEffect One mount'); - return () => - Scheduler.unstable_yieldValue('useLayoutEffect One unmount'); - }); + it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => { + function App({text}) { + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue('useLayoutEffect One mount'); + return () => + Scheduler.unstable_yieldValue('useLayoutEffect One unmount'); + }); + + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue('useLayoutEffect Two mount'); + return () => + Scheduler.unstable_yieldValue('useLayoutEffect Two unmount'); + }); + + return text; + } - React.useLayoutEffect(() => { - Scheduler.unstable_yieldValue('useLayoutEffect Two mount'); - return () => - Scheduler.unstable_yieldValue('useLayoutEffect Two unmount'); + ReactNoop.act(() => { + ReactNoop.render(); }); - return text; - } - - ReactNoop.act(() => { - ReactNoop.render(); - }); - - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toHaveYielded([ 'useLayoutEffect One mount', 'useLayoutEffect Two mount', @@ -369,191 +316,169 @@ describe('ReactDoubleInvokeEvents', () => { 'useLayoutEffect One mount', 'useLayoutEffect Two mount', ]); - } else { + + ReactNoop.act(() => { + ReactNoop.render(); + }); + expect(Scheduler).toHaveYielded([ + 'useLayoutEffect One unmount', + 'useLayoutEffect Two unmount', 'useLayoutEffect One mount', 'useLayoutEffect Two mount', ]); - } - - ReactNoop.act(() => { - ReactNoop.render(); - }); - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect One unmount', - 'useLayoutEffect Two unmount', - 'useLayoutEffect One mount', - 'useLayoutEffect Two mount', - ]); + ReactNoop.act(() => { + ReactNoop.render(null); + }); - ReactNoop.act(() => { - ReactNoop.render(null); + expect(Scheduler).toHaveYielded([ + 'useLayoutEffect One unmount', + 'useLayoutEffect Two unmount', + ]); }); - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect One unmount', - 'useLayoutEffect Two unmount', - ]); - }); - - it('useEffect and useLayoutEffect is called twice when there is no unmount', () => { - function App({text}) { - React.useEffect(() => { - Scheduler.unstable_yieldValue('useEffect mount'); - }); + it('useEffect and useLayoutEffect is called twice when there is no unmount', () => { + function App({text}) { + React.useEffect(() => { + Scheduler.unstable_yieldValue('useEffect mount'); + }); - React.useLayoutEffect(() => { - Scheduler.unstable_yieldValue('useLayoutEffect mount'); - }); + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue('useLayoutEffect mount'); + }); - return text; - } + return text; + } - ReactNoop.act(() => { - ReactNoop.render(); - }); + ReactNoop.act(() => { + ReactNoop.render(); + }); - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toHaveYielded([ 'useLayoutEffect mount', 'useEffect mount', 'useLayoutEffect mount', 'useEffect mount', ]); - } else { + + ReactNoop.act(() => { + ReactNoop.render(); + }); + expect(Scheduler).toHaveYielded([ 'useLayoutEffect mount', 'useEffect mount', ]); - } - ReactNoop.act(() => { - ReactNoop.render(); - }); - - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect mount', - 'useEffect mount', - ]); + ReactNoop.act(() => { + ReactNoop.render(null); + }); - ReactNoop.act(() => { - ReactNoop.render(null); + expect(Scheduler).toHaveYielded([]); }); - expect(Scheduler).toHaveYielded([]); - }); + it('passes the right context to class component lifecycles', () => { + class App extends React.PureComponent { + test() {} - it('passes the right context to class component lifecycles', () => { - class App extends React.PureComponent { - test() {} - - componentDidMount() { - this.test(); - Scheduler.unstable_yieldValue('componentDidMount'); - } + componentDidMount() { + this.test(); + Scheduler.unstable_yieldValue('componentDidMount'); + } - componentDidUpdate() { - this.test(); - Scheduler.unstable_yieldValue('componentDidUpdate'); - } + componentDidUpdate() { + this.test(); + Scheduler.unstable_yieldValue('componentDidUpdate'); + } - componentWillUnmount() { - this.test(); - Scheduler.unstable_yieldValue('componentWillUnmount'); - } + componentWillUnmount() { + this.test(); + Scheduler.unstable_yieldValue('componentWillUnmount'); + } - render() { - return null; + render() { + return null; + } } - } - ReactNoop.act(() => { - ReactNoop.render(); - }); + ReactNoop.act(() => { + ReactNoop.render(); + }); - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toHaveYielded([ 'componentDidMount', 'componentWillUnmount', 'componentDidMount', ]); - } else { - expect(Scheduler).toHaveYielded(['componentDidMount']); - } - }); + }); - it('double invoking works for class components', () => { - class App extends React.PureComponent { - componentDidMount() { - Scheduler.unstable_yieldValue('componentDidMount'); - } + it('double invoking works for class components', () => { + class App extends React.PureComponent { + componentDidMount() { + Scheduler.unstable_yieldValue('componentDidMount'); + } - componentDidUpdate() { - Scheduler.unstable_yieldValue('componentDidUpdate'); - } + componentDidUpdate() { + Scheduler.unstable_yieldValue('componentDidUpdate'); + } - componentWillUnmount() { - Scheduler.unstable_yieldValue('componentWillUnmount'); - } + componentWillUnmount() { + Scheduler.unstable_yieldValue('componentWillUnmount'); + } - render() { - return this.props.text; + render() { + return this.props.text; + } } - } - ReactNoop.act(() => { - ReactNoop.render(); - }); + ReactNoop.act(() => { + ReactNoop.render(); + }); - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toHaveYielded([ 'componentDidMount', 'componentWillUnmount', 'componentDidMount', ]); - } else { - expect(Scheduler).toHaveYielded(['componentDidMount']); - } - - ReactNoop.act(() => { - ReactNoop.render(); - }); - - expect(Scheduler).toHaveYielded(['componentDidUpdate']); - ReactNoop.act(() => { - ReactNoop.render(null); - }); + ReactNoop.act(() => { + ReactNoop.render(); + }); - expect(Scheduler).toHaveYielded(['componentWillUnmount']); - }); + expect(Scheduler).toHaveYielded(['componentDidUpdate']); - it('double flushing passive effects only results in one double invoke', () => { - function App({text}) { - const [state, setState] = React.useState(0); - React.useEffect(() => { - if (state !== 1) { - setState(1); - } - Scheduler.unstable_yieldValue('useEffect mount'); - return () => Scheduler.unstable_yieldValue('useEffect unmount'); + ReactNoop.act(() => { + ReactNoop.render(null); }); - React.useLayoutEffect(() => { - Scheduler.unstable_yieldValue('useLayoutEffect mount'); - return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount'); - }); + expect(Scheduler).toHaveYielded(['componentWillUnmount']); + }); - Scheduler.unstable_yieldValue(text); - return text; - } + it('double flushing passive effects only results in one double invoke', () => { + function App({text}) { + const [state, setState] = React.useState(0); + React.useEffect(() => { + if (state !== 1) { + setState(1); + } + Scheduler.unstable_yieldValue('useEffect mount'); + return () => Scheduler.unstable_yieldValue('useEffect unmount'); + }); + + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue('useLayoutEffect mount'); + return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount'); + }); + + Scheduler.unstable_yieldValue(text); + return text; + } - ReactNoop.act(() => { - ReactNoop.render(); - }); + ReactNoop.act(() => { + ReactNoop.render(); + }); - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toHaveYielded([ 'mount', 'useLayoutEffect mount', @@ -568,57 +493,44 @@ describe('ReactDoubleInvokeEvents', () => { 'useEffect unmount', 'useEffect mount', ]); - } else { - expect(Scheduler).toHaveYielded([ - 'mount', - 'useLayoutEffect mount', - 'useEffect mount', - 'mount', - 'useLayoutEffect unmount', - 'useLayoutEffect mount', - 'useEffect unmount', - 'useEffect mount', - ]); - } - }); - - it('newly mounted components after initial mount get double invoked', () => { - let _setShowChild; - function Child() { - React.useEffect(() => { - Scheduler.unstable_yieldValue('Child useEffect mount'); - return () => Scheduler.unstable_yieldValue('Child useEffect unmount'); - }); - React.useLayoutEffect(() => { - Scheduler.unstable_yieldValue('Child useLayoutEffect mount'); - return () => - Scheduler.unstable_yieldValue('Child useLayoutEffect unmount'); - }); + }); - return null; - } + it('newly mounted components after initial mount get double invoked', () => { + let _setShowChild; + function Child() { + React.useEffect(() => { + Scheduler.unstable_yieldValue('Child useEffect mount'); + return () => Scheduler.unstable_yieldValue('Child useEffect unmount'); + }); + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue('Child useLayoutEffect mount'); + return () => + Scheduler.unstable_yieldValue('Child useLayoutEffect unmount'); + }); - function App() { - const [showChild, setShowChild] = React.useState(false); - _setShowChild = setShowChild; - React.useEffect(() => { - Scheduler.unstable_yieldValue('App useEffect mount'); - return () => Scheduler.unstable_yieldValue('App useEffect unmount'); - }); - React.useLayoutEffect(() => { - Scheduler.unstable_yieldValue('App useLayoutEffect mount'); - return () => - Scheduler.unstable_yieldValue('App useLayoutEffect unmount'); - }); + return null; + } - return showChild && ; - } + function App() { + const [showChild, setShowChild] = React.useState(false); + _setShowChild = setShowChild; + React.useEffect(() => { + Scheduler.unstable_yieldValue('App useEffect mount'); + return () => Scheduler.unstable_yieldValue('App useEffect unmount'); + }); + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue('App useLayoutEffect mount'); + return () => + Scheduler.unstable_yieldValue('App useLayoutEffect unmount'); + }); + + return showChild && ; + } - ReactNoop.act(() => { - ReactNoop.render(); - }); + ReactNoop.act(() => { + ReactNoop.render(); + }); - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toHaveYielded([ 'App useLayoutEffect mount', 'App useEffect mount', @@ -627,18 +539,11 @@ describe('ReactDoubleInvokeEvents', () => { 'App useLayoutEffect mount', 'App useEffect mount', ]); - } else { - expect(Scheduler).toHaveYielded([ - 'App useLayoutEffect mount', - 'App useEffect mount', - ]); - } - ReactNoop.act(() => { - _setShowChild(true); - }); + ReactNoop.act(() => { + _setShowChild(true); + }); - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toHaveYielded([ 'App useLayoutEffect unmount', 'Child useLayoutEffect mount', @@ -651,59 +556,48 @@ describe('ReactDoubleInvokeEvents', () => { 'Child useLayoutEffect mount', 'Child useEffect mount', ]); - } else { - expect(Scheduler).toHaveYielded([ - 'App useLayoutEffect unmount', - 'Child useLayoutEffect mount', - 'App useLayoutEffect mount', - 'App useEffect unmount', - 'Child useEffect mount', - 'App useEffect mount', - ]); - } - }); + }); - it('classes and functions are double invoked together correctly', () => { - class ClassChild extends React.PureComponent { - componentDidMount() { - Scheduler.unstable_yieldValue('componentDidMount'); + it('classes and functions are double invoked together correctly', () => { + class ClassChild extends React.PureComponent { + componentDidMount() { + Scheduler.unstable_yieldValue('componentDidMount'); + } + + componentWillUnmount() { + Scheduler.unstable_yieldValue('componentWillUnmount'); + } + + render() { + return this.props.text; + } } - componentWillUnmount() { - Scheduler.unstable_yieldValue('componentWillUnmount'); + function FunctionChild({text}) { + React.useEffect(() => { + Scheduler.unstable_yieldValue('useEffect mount'); + return () => Scheduler.unstable_yieldValue('useEffect unmount'); + }); + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue('useLayoutEffect mount'); + return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount'); + }); + return text; } - render() { - return this.props.text; + function App({text}) { + return ( + <> + + + + ); } - } - function FunctionChild({text}) { - React.useEffect(() => { - Scheduler.unstable_yieldValue('useEffect mount'); - return () => Scheduler.unstable_yieldValue('useEffect unmount'); - }); - React.useLayoutEffect(() => { - Scheduler.unstable_yieldValue('useLayoutEffect mount'); - return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount'); + ReactNoop.act(() => { + ReactNoop.render(); }); - return text; - } - - function App({text}) { - return ( - <> - - - - ); - } - ReactNoop.act(() => { - ReactNoop.render(); - }); - - if (shouldDoubleInvokingEffects()) { expect(Scheduler).toHaveYielded([ 'componentDidMount', 'useLayoutEffect mount', @@ -715,33 +609,27 @@ describe('ReactDoubleInvokeEvents', () => { 'useLayoutEffect mount', 'useEffect mount', ]); - } else { + + ReactNoop.act(() => { + ReactNoop.render(); + }); + expect(Scheduler).toHaveYielded([ - 'componentDidMount', + 'useLayoutEffect unmount', 'useLayoutEffect mount', + 'useEffect unmount', 'useEffect mount', ]); - } - ReactNoop.act(() => { - ReactNoop.render(); - }); - - expect(Scheduler).toHaveYielded([ - 'useLayoutEffect unmount', - 'useLayoutEffect mount', - 'useEffect unmount', - 'useEffect mount', - ]); + ReactNoop.act(() => { + ReactNoop.render(null); + }); - ReactNoop.act(() => { - ReactNoop.render(null); + expect(Scheduler).toHaveYielded([ + 'componentWillUnmount', + 'useLayoutEffect unmount', + 'useEffect unmount', + ]); }); - - expect(Scheduler).toHaveYielded([ - 'componentWillUnmount', - 'useLayoutEffect unmount', - 'useEffect unmount', - ]); - }); + } }); diff --git a/packages/react/src/__tests__/ReactProfiler-test.internal.js b/packages/react/src/__tests__/ReactProfiler-test.internal.js index a2e27073a3f79..d6e44ab820960 100644 --- a/packages/react/src/__tests__/ReactProfiler-test.internal.js +++ b/packages/react/src/__tests__/ReactProfiler-test.internal.js @@ -4875,7 +4875,8 @@ describe('Profiler', () => { if (__DEV__) { it('double invoking does not disconnect wrapped async work', () => { - ReactFeatureFlags.enableDoubleInvokingEffects = true; + ReactFeatureFlags.enableStrictEffects = true; + ReactFeatureFlags.createRootStrictEffectsByDefault = true; const callback = jest.fn(() => { const wrappedInteractions = SchedulerTracing.unstable_getCurrent(); diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 43381ed8b8b06..953eb7cd56615 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -25,7 +25,11 @@ export const debugRenderPhaseSideEffectsForStrictMode = __DEV__; // Helps identify code that is not safe for planned Offscreen API and Suspense semantics; // this feature flag only impacts StrictEffectsMode. -export const enableDoubleInvokingEffects = false; +export const enableStrictEffects = false; + +// If TRUE, trees rendered with createRoot (and createBlockingRoot) APIs will be StrictEffectsMode. +// If FALSE, these trees will be StrictLegacyMode. +export const createRootStrictEffectsByDefault = false; // To preserve the "Pause on caught exceptions" behavior of the debugger, we // replay the begin phase of a failed component inside invokeGuardedCallback. diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 6544be4cbe040..2f4406b2ceafa 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -52,7 +52,8 @@ export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableDoubleInvokingEffects = false; +export const enableStrictEffects = false; +export const createRootStrictEffectsByDefault = false; export const enableUseRefAccessWarning = false; export const enableRecursiveCommitTraversal = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index a6482da52d524..35e1067646d94 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -51,7 +51,8 @@ export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableDoubleInvokingEffects = false; +export const enableStrictEffects = false; +export const createRootStrictEffectsByDefault = false; export const enableUseRefAccessWarning = false; export const enableRecursiveCommitTraversal = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 343bb609b4d25..67ecdb72983c7 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -51,7 +51,8 @@ export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableDoubleInvokingEffects = false; +export const enableStrictEffects = false; +export const createRootStrictEffectsByDefault = false; export const enableUseRefAccessWarning = false; export const enableRecursiveCommitTraversal = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js index e9ad4240a6fc3..9ed0be59689e7 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js @@ -51,7 +51,8 @@ export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableDoubleInvokingEffects = false; +export const enableStrictEffects = false; +export const createRootStrictEffectsByDefault = false; export const enableUseRefAccessWarning = false; export const enableRecursiveCommitTraversal = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 2afc959952aca..2e7513a8ddf15 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -51,7 +51,8 @@ export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableDoubleInvokingEffects = true; +export const enableStrictEffects = true; +export const createRootStrictEffectsByDefault = false; export const enableUseRefAccessWarning = false; export const enableRecursiveCommitTraversal = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index d79a63c577892..5a362e8a31419 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -51,7 +51,8 @@ export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableDoubleInvokingEffects = false; +export const enableStrictEffects = false; +export const createRootStrictEffectsByDefault = false; export const enableUseRefAccessWarning = false; export const enableRecursiveCommitTraversal = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index 732360618f6bf..54942f0c7cd2d 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -51,7 +51,8 @@ export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = true; -export const enableDoubleInvokingEffects = false; +export const enableStrictEffects = false; +export const createRootStrictEffectsByDefault = false; export const enableUseRefAccessWarning = false; export const enableRecursiveCommitTraversal = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index daf49b2ab2255..1d9f89699f317 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -50,7 +50,8 @@ export const enableTrustedTypesIntegration = false; export const disableSchedulerTimeoutBasedOnReactExpirationTime = false; export const disableNativeComponentFrames = false; -export const enableDoubleInvokingEffects = false; +export const createRootStrictEffectsByDefault = false; +export const enableStrictEffects = false; export const enableUseRefAccessWarning = __VARIANT__; export const enableProfilerNestedUpdateScheduledHook = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 2d983a3e8f81d..5e5cd2105b383 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -27,7 +27,8 @@ export const { decoupleUpdatePriorityFromScheduler, enableDebugTracing, skipUnmountedBoundaries, - enableDoubleInvokingEffects, + enableStrictEffects, + createRootStrictEffectsByDefault, enableUseRefAccessWarning, disableNativeComponentFrames, disableSchedulerTimeoutInWorkLoop, From 2634f5a253f217d85ae433a5597ca671408ce09d Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 24 Feb 2021 15:06:25 -0500 Subject: [PATCH 5/5] Add StrictMode 'unstable_level' prop and createRoot 'unstable_strictModeLevel' option New StrictMode 'unstable_level' prop allows specifying which level of strict mode to use. If no level attribute is specified, StrictLegacyMode will be used to maintain backwards compatibility. Otherwise the following is true: * Level 0 does nothing * Level 1 selects StrictLegacyMode * Level 2 selects StrictEffectsMode (which includes StrictLegacyMode) Levels can be increased with nesting (0 -> 1 -> 2) but not decreased. This commit also adds a new 'unstable_strictModeLevel' option to the createRoot and createBatchedRoot APIs. This option can be used to override default behavior to increase or decrease the StrictMode level of the root. A subsequent commit will add additional DEV warnings: * If a nested StrictMode tag attempts to explicitly decrease the level * If a level attribute changes in an update --- packages/react-dom/src/client/ReactDOMRoot.js | 14 +- .../react-native-renderer/src/ReactFabric.js | 2 +- .../src/ReactNativeRenderer.js | 2 +- .../src/createReactNoop.js | 5 +- .../react-reconciler/src/ReactFiber.new.js | 59 +++- .../react-reconciler/src/ReactFiber.old.js | 59 +++- .../src/ReactFiberReconciler.new.js | 9 +- .../src/ReactFiberReconciler.old.js | 9 +- .../src/ReactFiberRoot.new.js | 3 +- .../src/ReactFiberRoot.old.js | 3 +- .../src/ReactFiberWorkLoop.new.js | 8 +- .../src/ReactFiberWorkLoop.old.js | 8 +- .../src/ReactTestRenderer.js | 1 + .../ReactStrictMode-test.internal.js | 264 ++++++++++++++++++ 14 files changed, 408 insertions(+), 38 deletions(-) create mode 100644 packages/react/src/__tests__/ReactStrictMode-test.internal.js diff --git a/packages/react-dom/src/client/ReactDOMRoot.js b/packages/react-dom/src/client/ReactDOMRoot.js index 66a31328540e5..62a72dc229618 100644 --- a/packages/react-dom/src/client/ReactDOMRoot.js +++ b/packages/react-dom/src/client/ReactDOMRoot.js @@ -27,6 +27,7 @@ export type RootOptions = { mutableSources?: Array>, ... }, + unstable_strictModeLevel?: number, ... }; @@ -128,7 +129,18 @@ function createRootImpl( options.hydrationOptions != null && options.hydrationOptions.mutableSources) || null; - const root = createContainer(container, tag, hydrate, hydrationCallbacks); + const strictModeLevelOverride = + options != null && options.unstable_strictModeLevel != null + ? options.unstable_strictModeLevel + : null; + + const root = createContainer( + container, + tag, + hydrate, + hydrationCallbacks, + strictModeLevelOverride, + ); markContainerAsRoot(root.current, container); const rootContainerElement = diff --git a/packages/react-native-renderer/src/ReactFabric.js b/packages/react-native-renderer/src/ReactFabric.js index 87fe4e8e2bf1f..03ad183c37df4 100644 --- a/packages/react-native-renderer/src/ReactFabric.js +++ b/packages/react-native-renderer/src/ReactFabric.js @@ -203,7 +203,7 @@ function render( if (!root) { // TODO (bvaughn): If we decide to keep the wrapper component, // We could create a wrapper for containerTag as well to reduce special casing. - root = createContainer(containerTag, LegacyRoot, false, null); + root = createContainer(containerTag, LegacyRoot, false, null, null); roots.set(containerTag, root); } updateContainer(element, root, null, callback); diff --git a/packages/react-native-renderer/src/ReactNativeRenderer.js b/packages/react-native-renderer/src/ReactNativeRenderer.js index fdf9ebf5ac597..673482d0175cd 100644 --- a/packages/react-native-renderer/src/ReactNativeRenderer.js +++ b/packages/react-native-renderer/src/ReactNativeRenderer.js @@ -202,7 +202,7 @@ function render( if (!root) { // TODO (bvaughn): If we decide to keep the wrapper component, // We could create a wrapper for containerTag as well to reduce special casing. - root = createContainer(containerTag, LegacyRoot, false, null); + root = createContainer(containerTag, LegacyRoot, false, null, null); roots.set(containerTag, root); } updateContainer(element, root, null, callback); diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index 26c58ffd8445c..743470966b3f0 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -722,7 +722,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { if (!root) { const container = {rootID: rootID, pendingChildren: [], children: []}; rootContainers.set(rootID, container); - root = NoopRenderer.createContainer(container, tag, false, null); + root = NoopRenderer.createContainer(container, tag, false, null, null); roots.set(rootID, root); } return root.current.stateNode.containerInfo; @@ -740,6 +740,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { ConcurrentRoot, false, null, + null, ); return { _Scheduler: Scheduler, @@ -766,6 +767,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { BlockingRoot, false, null, + null, ); return { _Scheduler: Scheduler, @@ -792,6 +794,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { LegacyRoot, false, null, + null, ); return { _Scheduler: Scheduler, diff --git a/packages/react-reconciler/src/ReactFiber.new.js b/packages/react-reconciler/src/ReactFiber.new.js index d1a4837fdba01..f1a0f56b23409 100644 --- a/packages/react-reconciler/src/ReactFiber.new.js +++ b/packages/react-reconciler/src/ReactFiber.new.js @@ -421,20 +421,46 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) { return workInProgress; } -export function createHostRootFiber(tag: RootTag): Fiber { +export function createHostRootFiber( + tag: RootTag, + strictModeLevelOverride: null | number, +): Fiber { let mode; if (tag === ConcurrentRoot) { - if (enableStrictEffects && createRootStrictEffectsByDefault) { - mode = - ConcurrentMode | BlockingMode | StrictLegacyMode | StrictEffectsMode; + mode = ConcurrentMode | BlockingMode; + if (strictModeLevelOverride !== null) { + if (strictModeLevelOverride >= 1) { + mode |= StrictLegacyMode; + } + if (enableStrictEffects) { + if (strictModeLevelOverride >= 2) { + mode |= StrictEffectsMode; + } + } } else { - mode = ConcurrentMode | BlockingMode | StrictLegacyMode; + if (enableStrictEffects && createRootStrictEffectsByDefault) { + mode |= StrictLegacyMode | StrictEffectsMode; + } else { + mode |= StrictLegacyMode; + } } } else if (tag === BlockingRoot) { - if (enableStrictEffects && createRootStrictEffectsByDefault) { - mode = BlockingMode | StrictLegacyMode | StrictEffectsMode; + mode = BlockingMode; + if (strictModeLevelOverride !== null) { + if (strictModeLevelOverride >= 1) { + mode |= StrictLegacyMode; + } + if (enableStrictEffects) { + if (strictModeLevelOverride >= 2) { + mode |= StrictEffectsMode; + } + } } else { - mode = BlockingMode | StrictLegacyMode; + if (enableStrictEffects && createRootStrictEffectsByDefault) { + mode |= StrictLegacyMode | StrictEffectsMode; + } else { + mode |= StrictLegacyMode; + } } } else { mode = NoMode; @@ -484,8 +510,21 @@ export function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = Mode; - // TODO (StrictEffectsMode) Add support for new strict mode "level" attribute - mode |= StrictLegacyMode; + + // Legacy strict mode ( without any level prop) defaults to level 1. + const level = + pendingProps.unstable_level == null ? 1 : pendingProps.unstable_level; + + // Levels cascade; higher levels inherit all lower level modes. + // It is explicitly not supported to lower a mode with nesting, only to increase it. + if (level >= 1) { + mode |= StrictLegacyMode; + } + if (enableStrictEffects) { + if (level >= 2) { + mode |= StrictEffectsMode; + } + } break; case REACT_PROFILER_TYPE: return createFiberFromProfiler(pendingProps, mode, lanes, key); diff --git a/packages/react-reconciler/src/ReactFiber.old.js b/packages/react-reconciler/src/ReactFiber.old.js index 6f79f0331d348..6419fd6b261b3 100644 --- a/packages/react-reconciler/src/ReactFiber.old.js +++ b/packages/react-reconciler/src/ReactFiber.old.js @@ -421,20 +421,46 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) { return workInProgress; } -export function createHostRootFiber(tag: RootTag): Fiber { +export function createHostRootFiber( + tag: RootTag, + strictModeLevelOverride: null | number, +): Fiber { let mode; if (tag === ConcurrentRoot) { - if (enableStrictEffects && createRootStrictEffectsByDefault) { - mode = - ConcurrentMode | BlockingMode | StrictLegacyMode | StrictEffectsMode; + mode = ConcurrentMode | BlockingMode; + if (strictModeLevelOverride !== null) { + if (strictModeLevelOverride >= 1) { + mode |= StrictLegacyMode; + } + if (enableStrictEffects) { + if (strictModeLevelOverride >= 2) { + mode |= StrictEffectsMode; + } + } } else { - mode = ConcurrentMode | BlockingMode | StrictLegacyMode; + if (enableStrictEffects && createRootStrictEffectsByDefault) { + mode |= StrictLegacyMode | StrictEffectsMode; + } else { + mode |= StrictLegacyMode; + } } } else if (tag === BlockingRoot) { - if (enableStrictEffects && createRootStrictEffectsByDefault) { - mode = BlockingMode | StrictLegacyMode | StrictEffectsMode; + mode = BlockingMode; + if (strictModeLevelOverride !== null) { + if (strictModeLevelOverride >= 1) { + mode |= StrictLegacyMode; + } + if (enableStrictEffects) { + if (strictModeLevelOverride >= 2) { + mode |= StrictEffectsMode; + } + } } else { - mode = BlockingMode | StrictLegacyMode; + if (enableStrictEffects && createRootStrictEffectsByDefault) { + mode |= StrictLegacyMode | StrictEffectsMode; + } else { + mode |= StrictLegacyMode; + } } } else { mode = NoMode; @@ -484,8 +510,21 @@ export function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = Mode; - // TODO (StrictEffectsMode) Add support for new strict mode "level" attribute - mode |= StrictLegacyMode; + + // Legacy strict mode ( without any level prop) defaults to level 1. + const level = + pendingProps.unstable_level == null ? 1 : pendingProps.unstable_level; + + // Levels cascade; higher levels inherit all lower level modes. + // It is explicitly not supported to lower a mode with nesting, only to increase it. + if (level >= 1) { + mode |= StrictLegacyMode; + } + if (enableStrictEffects) { + if (level >= 2) { + mode |= StrictEffectsMode; + } + } break; case REACT_PROFILER_TYPE: return createFiberFromProfiler(pendingProps, mode, lanes, key); diff --git a/packages/react-reconciler/src/ReactFiberReconciler.new.js b/packages/react-reconciler/src/ReactFiberReconciler.new.js index 3b4edc51e28e8..cc639fa0865f6 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.new.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.new.js @@ -256,8 +256,15 @@ export function createContainer( tag: RootTag, hydrate: boolean, hydrationCallbacks: null | SuspenseHydrationCallbacks, + strictModeLevelOverride: null | number, ): OpaqueRoot { - return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks); + return createFiberRoot( + containerInfo, + tag, + hydrate, + hydrationCallbacks, + strictModeLevelOverride, + ); } export function updateContainer( diff --git a/packages/react-reconciler/src/ReactFiberReconciler.old.js b/packages/react-reconciler/src/ReactFiberReconciler.old.js index ecbcbd8a9e5e3..3044bf934daf8 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.old.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.old.js @@ -256,8 +256,15 @@ export function createContainer( tag: RootTag, hydrate: boolean, hydrationCallbacks: null | SuspenseHydrationCallbacks, + strictModeLevelOverride: null | number, ): OpaqueRoot { - return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks); + return createFiberRoot( + containerInfo, + tag, + hydrate, + hydrationCallbacks, + strictModeLevelOverride, + ); } export function updateContainer( diff --git a/packages/react-reconciler/src/ReactFiberRoot.new.js b/packages/react-reconciler/src/ReactFiberRoot.new.js index 9057137ec61a8..ae6a5bdb596f6 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.new.js +++ b/packages/react-reconciler/src/ReactFiberRoot.new.js @@ -91,6 +91,7 @@ export function createFiberRoot( tag: RootTag, hydrate: boolean, hydrationCallbacks: null | SuspenseHydrationCallbacks, + strictModeLevelOverride: null | number, ): FiberRoot { const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any); if (enableSuspenseCallback) { @@ -99,7 +100,7 @@ export function createFiberRoot( // Cyclic construction. This cheats the type system right now because // stateNode is any. - const uninitializedFiber = createHostRootFiber(tag); + const uninitializedFiber = createHostRootFiber(tag, strictModeLevelOverride); root.current = uninitializedFiber; uninitializedFiber.stateNode = root; diff --git a/packages/react-reconciler/src/ReactFiberRoot.old.js b/packages/react-reconciler/src/ReactFiberRoot.old.js index 92ec811dd5589..0c0d45c098720 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.old.js +++ b/packages/react-reconciler/src/ReactFiberRoot.old.js @@ -91,6 +91,7 @@ export function createFiberRoot( tag: RootTag, hydrate: boolean, hydrationCallbacks: null | SuspenseHydrationCallbacks, + strictModeLevelOverride: null | number, ): FiberRoot { const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any); if (enableSuspenseCallback) { @@ -99,7 +100,7 @@ export function createFiberRoot( // Cyclic construction. This cheats the type system right now because // stateNode is any. - const uninitializedFiber = createHostRootFiber(tag); + const uninitializedFiber = createHostRootFiber(tag, strictModeLevelOverride); root.current = uninitializedFiber; uninitializedFiber.stateNode = root; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index ebc9fb8f17605..7caee07872517 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -105,7 +105,6 @@ import { import { NoMode, StrictLegacyMode, - StrictEffectsMode, ProfileMode, BlockingMode, ConcurrentMode, @@ -2562,10 +2561,9 @@ function commitDoubleInvokeEffectsInDEV( hasPassiveEffects: boolean, ) { if (__DEV__ && enableStrictEffects) { - // Never double-invoke effects outside of StrictEffectsMode. - if ((fiber.mode & StrictEffectsMode) === NoMode) { - return; - } + // TODO (StrictEffects) Should we set a marker on the root if it contains strict effects + // so we don't traverse unnecessarily? similar to subtreeFlags but just at the root level. + // Maybe not a big deal since this is DEV only behavior. setCurrentDebugFiberInDEV(fiber); invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV); diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 25b77701d8699..1467929ce4597 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -105,7 +105,6 @@ import { import { NoMode, StrictLegacyMode, - StrictEffectsMode, ProfileMode, BlockingMode, ConcurrentMode, @@ -2562,10 +2561,9 @@ function commitDoubleInvokeEffectsInDEV( hasPassiveEffects: boolean, ) { if (__DEV__ && enableStrictEffects) { - // Never double-invoke effects outside of StrictEffectsMode. - if ((fiber.mode & StrictEffectsMode) === NoMode) { - return; - } + // TODO (StrictEffects) Should we set a marker on the root if it contains strict effects + // so we don't traverse unnecessarily? similar to subtreeFlags but just at the root level. + // Maybe not a big deal since this is DEV only behavior. setCurrentDebugFiberInDEV(fiber); invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV); diff --git a/packages/react-test-renderer/src/ReactTestRenderer.js b/packages/react-test-renderer/src/ReactTestRenderer.js index c4eea12e8a550..ff80440379f14 100644 --- a/packages/react-test-renderer/src/ReactTestRenderer.js +++ b/packages/react-test-renderer/src/ReactTestRenderer.js @@ -451,6 +451,7 @@ function create(element: React$Element, options: TestRendererOptions) { isConcurrent ? ConcurrentRoot : LegacyRoot, false, null, + null, ); invariant(root != null, 'something went wrong'); updateContainer(element, root, null, null); diff --git a/packages/react/src/__tests__/ReactStrictMode-test.internal.js b/packages/react/src/__tests__/ReactStrictMode-test.internal.js new file mode 100644 index 0000000000000..cb567340d3983 --- /dev/null +++ b/packages/react/src/__tests__/ReactStrictMode-test.internal.js @@ -0,0 +1,264 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails react-core + */ + +'use strict'; + +describe('ReactStrictMode', () => { + let React; + let ReactDOM; + let act; + + beforeEach(() => { + jest.resetModules(); + React = require('react'); + ReactDOM = require('react-dom'); + + const TestUtils = require('react-dom/test-utils'); + act = TestUtils.unstable_concurrentAct; + + const ReactFeatureFlags = require('shared/ReactFeatureFlags'); + ReactFeatureFlags.enableStrictEffects = __DEV__; + }); + + describe('levels', () => { + let log; + + beforeEach(() => { + log = []; + }); + + function Component({label}) { + React.useEffect(() => { + log.push(`${label}: useEffect mount`); + return () => log.push(`${label}: useEffect unmount`); + }); + + React.useLayoutEffect(() => { + log.push(`${label}: useLayoutEffect mount`); + return () => log.push(`${label}: useLayoutEffect unmount`); + }); + + log.push(`${label}: render`); + + return null; + } + + // @gate experimental + it('should support overriding default via createRoot option', () => { + act(() => { + const container = document.createElement('div'); + const root = ReactDOM.createRoot(container, { + unstable_strictModeLevel: 0, + }); + root.render(); + }); + + expect(log).toEqual([ + 'A: render', + 'A: useLayoutEffect mount', + 'A: useEffect mount', + ]); + }); + + // @gate experimental + it('should support overriding default via createBlockingRoot option', () => { + act(() => { + const container = document.createElement('div'); + const root = ReactDOM.createBlockingRoot(container, { + unstable_strictModeLevel: 0, + }); + root.render(); + }); + + expect(log).toEqual([ + 'A: render', + 'A: useLayoutEffect mount', + 'A: useEffect mount', + ]); + }); + + // @gate experimental + it('should disable strict mode if level 0 is specified', () => { + act(() => { + const container = document.createElement('div'); + const root = ReactDOM.createRoot(container, { + unstable_strictModeLevel: 0, + }); + root.render( + + + , + ); + }); + + expect(log).toEqual([ + 'A: render', + 'A: useLayoutEffect mount', + 'A: useEffect mount', + ]); + }); + + if (__DEV__) { + // @gate experimental + it('should default to level 1 (legacy mode)', () => { + act(() => { + const container = document.createElement('div'); + const root = ReactDOM.createRoot(container); + root.render( + + + , + ); + }); + + expect(log).toEqual([ + 'A: render', + 'A: render', + 'A: useLayoutEffect mount', + 'A: useEffect mount', + ]); + }); + + // @gate experimental + it('should support level 1 (legacy mode)', () => { + act(() => { + const container = document.createElement('div'); + const root = ReactDOM.createRoot(container); + root.render( + + + , + ); + }); + + expect(log).toEqual([ + 'A: render', + 'A: render', + 'A: useLayoutEffect mount', + 'A: useEffect mount', + ]); + }); + + // @gate experimental + it('should support level 2 (legacy + strict effects mode)', () => { + act(() => { + const container = document.createElement('div'); + const root = ReactDOM.createRoot(container); + root.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 experimental + it('should allow level to be increased with nesting', () => { + act(() => { + const container = document.createElement('div'); + const root = ReactDOM.createRoot(container, { + unstable_strictModeLevel: 0, + }); + root.render( + <> + + + + + + + , + + , + , + ); + }); + + expect(log).toEqual([ + 'A: render', + 'B: render', + 'B: render', + 'C: render', + 'C: render', + 'A: useLayoutEffect mount', + 'B: useLayoutEffect mount', + 'C: useLayoutEffect mount', + 'A: useEffect mount', + 'B: useEffect mount', + 'C: useEffect mount', + 'C: useLayoutEffect unmount', + 'C: useEffect unmount', + 'C: useLayoutEffect mount', + 'C: useEffect mount', + ]); + }); + + // @gate experimental + it('should not allow level to be decreased with nesting', () => { + act(() => { + const container = document.createElement('div'); + const root = ReactDOM.createRoot(container, { + unstable_strictModeLevel: 2, + }); + root.render( + <> + + + + + + + , + + , + , + ); + }); + + expect(log).toEqual([ + 'A: render', + 'A: render', + 'B: render', + 'B: render', + 'C: render', + 'C: render', + 'A: useLayoutEffect mount', + 'B: useLayoutEffect mount', + 'C: useLayoutEffect mount', + 'A: useEffect mount', + 'B: useEffect mount', + 'C: useEffect mount', + 'A: useLayoutEffect unmount', + 'B: useLayoutEffect unmount', + 'C: useLayoutEffect unmount', + 'A: useEffect unmount', + 'B: useEffect unmount', + 'C: useEffect unmount', + 'A: useLayoutEffect mount', + 'B: useLayoutEffect mount', + 'C: useLayoutEffect mount', + 'A: useEffect mount', + 'B: useEffect mount', + 'C: useEffect mount', + ]); + }); + } + }); +});