From 15fb8c3045064e13e81706a36bf0e4e419803c97 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 3 May 2021 16:57:03 -0400 Subject: [PATCH] createRoot API is no longer strict by default (#21417) --- .../src/__tests__/ReactTestUtilsAct-test.js | 14 +- packages/react-dom/src/client/ReactDOMRoot.js | 6 - .../react-native-renderer/src/ReactFabric.js | 2 +- .../src/ReactNativeRenderer.js | 2 +- .../src/createReactNoop.js | 4 +- .../react-reconciler/src/ReactFiber.new.js | 18 +- .../react-reconciler/src/ReactFiber.old.js | 18 +- .../src/ReactFiberReconciler.new.js | 2 - .../src/ReactFiberReconciler.old.js | 2 - .../src/ReactFiberRoot.new.js | 2 - .../src/ReactFiberRoot.old.js | 2 - .../__tests__/DebugTracing-test.internal.js | 2 - .../ReactHooksWithNoopRenderer-test.js | 27 +-- .../src/__tests__/ReactIncremental-test.js | 221 ++++++------------ ...tIncrementalErrorHandling-test.internal.js | 11 +- .../ReactIncrementalReflection-test.js | 24 +- .../ReactIncrementalSideEffects-test.js | 4 +- .../__tests__/ReactIncrementalUpdates-test.js | 7 +- .../src/__tests__/ReactNewContext-test.js | 11 +- .../useMutableSource-test.internal.js | 7 +- .../src/ReactTestRenderer.js | 6 - .../ReactStrictMode-test.internal.js | 48 +--- .../src/__tests__/ReactStrictMode-test.js | 50 ++-- .../react/src/__tests__/forwardRef-test.js | 28 +-- 24 files changed, 148 insertions(+), 370 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js b/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js index 6ce9981fde92f..8419c93c38c41 100644 --- a/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js +++ b/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js @@ -97,16 +97,10 @@ describe('ReactTestUtils.act()', () => { }); // @gate experimental - it('warns in concurrent mode', () => { - expect(() => { - const root = ReactDOM.unstable_createRoot( - document.createElement('div'), - ); - root.render(); - Scheduler.unstable_flushAll(); - }).toErrorDev([ - 'An update to App ran an effect, but was not wrapped in act(...)', - ]); + it('does not warn in concurrent mode', () => { + const root = ReactDOM.unstable_createRoot(document.createElement('div')); + root.render(); + Scheduler.unstable_flushAll(); }); }); }); diff --git a/packages/react-dom/src/client/ReactDOMRoot.js b/packages/react-dom/src/client/ReactDOMRoot.js index 0a78f61ad0977..73e96eb086bbc 100644 --- a/packages/react-dom/src/client/ReactDOMRoot.js +++ b/packages/react-dom/src/client/ReactDOMRoot.js @@ -27,7 +27,6 @@ export type RootOptions = { mutableSources?: Array>, ... }, - unstable_strictModeLevel?: number, unstable_concurrentUpdatesByDefault?: boolean, ... }; @@ -123,10 +122,6 @@ function createRootImpl( options.hydrationOptions != null && options.hydrationOptions.mutableSources) || null; - const strictModeLevelOverride = - options != null && options.unstable_strictModeLevel != null - ? options.unstable_strictModeLevel - : null; let concurrentUpdatesByDefaultOverride = null; if (allowConcurrentByDefault) { @@ -141,7 +136,6 @@ function createRootImpl( tag, hydrate, hydrationCallbacks, - strictModeLevelOverride, concurrentUpdatesByDefaultOverride, ); markContainerAsRoot(root.current, container); diff --git a/packages/react-native-renderer/src/ReactFabric.js b/packages/react-native-renderer/src/ReactFabric.js index b319cb22c7b7b..9f1f44f51cf49 100644 --- a/packages/react-native-renderer/src/ReactFabric.js +++ b/packages/react-native-renderer/src/ReactFabric.js @@ -207,7 +207,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, null, 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 4f73377320378..de42c6e41a58c 100644 --- a/packages/react-native-renderer/src/ReactNativeRenderer.js +++ b/packages/react-native-renderer/src/ReactNativeRenderer.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, null, 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 9d2c87621bcef..659f4d63fcc03 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, null); + root = NoopRenderer.createContainer(container, tag, false, null); roots.set(rootID, root); } return root.current.stateNode.containerInfo; @@ -740,7 +740,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { ConcurrentRoot, false, null, - null, ); return { _Scheduler: Scheduler, @@ -767,7 +766,6 @@ 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 c883079028a53..fcb757b96a651 100644 --- a/packages/react-reconciler/src/ReactFiber.new.js +++ b/packages/react-reconciler/src/ReactFiber.new.js @@ -422,27 +422,13 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) { export function createHostRootFiber( tag: RootTag, - strictModeLevelOverride: null | number, concurrentUpdatesByDefaultOverride: null | boolean, ): Fiber { let mode; if (tag === ConcurrentRoot) { mode = ConcurrentMode; - if (strictModeLevelOverride !== null) { - if (strictModeLevelOverride >= 1) { - mode |= StrictLegacyMode; - } - if (enableStrictEffects) { - if (strictModeLevelOverride >= 2) { - mode |= StrictEffectsMode; - } - } - } else { - if (enableStrictEffects && createRootStrictEffectsByDefault) { - mode |= StrictLegacyMode | StrictEffectsMode; - } else { - mode |= StrictLegacyMode; - } + if (enableStrictEffects && createRootStrictEffectsByDefault) { + mode |= StrictLegacyMode | StrictEffectsMode; } if ( // We only use this flag for our repo tests to check both behaviors. diff --git a/packages/react-reconciler/src/ReactFiber.old.js b/packages/react-reconciler/src/ReactFiber.old.js index e33c0e7cd835f..50947ff94c8a1 100644 --- a/packages/react-reconciler/src/ReactFiber.old.js +++ b/packages/react-reconciler/src/ReactFiber.old.js @@ -422,27 +422,13 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) { export function createHostRootFiber( tag: RootTag, - strictModeLevelOverride: null | number, concurrentUpdatesByDefaultOverride: null | boolean, ): Fiber { let mode; if (tag === ConcurrentRoot) { mode = ConcurrentMode; - if (strictModeLevelOverride !== null) { - if (strictModeLevelOverride >= 1) { - mode |= StrictLegacyMode; - } - if (enableStrictEffects) { - if (strictModeLevelOverride >= 2) { - mode |= StrictEffectsMode; - } - } - } else { - if (enableStrictEffects && createRootStrictEffectsByDefault) { - mode |= StrictLegacyMode | StrictEffectsMode; - } else { - mode |= StrictLegacyMode; - } + if (enableStrictEffects && createRootStrictEffectsByDefault) { + mode |= StrictLegacyMode | StrictEffectsMode; } if ( // We only use this flag for our repo tests to check both behaviors. diff --git a/packages/react-reconciler/src/ReactFiberReconciler.new.js b/packages/react-reconciler/src/ReactFiberReconciler.new.js index f53a24e86c60b..b19223db377a4 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.new.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.new.js @@ -248,7 +248,6 @@ export function createContainer( tag: RootTag, hydrate: boolean, hydrationCallbacks: null | SuspenseHydrationCallbacks, - strictModeLevelOverride: null | number, concurrentUpdatesByDefaultOverride: null | boolean, ): OpaqueRoot { return createFiberRoot( @@ -256,7 +255,6 @@ export function createContainer( tag, hydrate, hydrationCallbacks, - strictModeLevelOverride, concurrentUpdatesByDefaultOverride, ); } diff --git a/packages/react-reconciler/src/ReactFiberReconciler.old.js b/packages/react-reconciler/src/ReactFiberReconciler.old.js index 00c12b3406df8..fe528b8316c82 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.old.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.old.js @@ -248,7 +248,6 @@ export function createContainer( tag: RootTag, hydrate: boolean, hydrationCallbacks: null | SuspenseHydrationCallbacks, - strictModeLevelOverride: null | number, concurrentUpdatesByDefaultOverride: null | boolean, ): OpaqueRoot { return createFiberRoot( @@ -256,7 +255,6 @@ export function createContainer( tag, hydrate, hydrationCallbacks, - strictModeLevelOverride, concurrentUpdatesByDefaultOverride, ); } diff --git a/packages/react-reconciler/src/ReactFiberRoot.new.js b/packages/react-reconciler/src/ReactFiberRoot.new.js index d2efcc5851d49..ae62134de1bc1 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.new.js +++ b/packages/react-reconciler/src/ReactFiberRoot.new.js @@ -98,7 +98,6 @@ export function createFiberRoot( tag: RootTag, hydrate: boolean, hydrationCallbacks: null | SuspenseHydrationCallbacks, - strictModeLevelOverride: null | number, concurrentUpdatesByDefaultOverride: null | boolean, ): FiberRoot { const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any); @@ -110,7 +109,6 @@ export function createFiberRoot( // stateNode is any. const uninitializedFiber = createHostRootFiber( tag, - strictModeLevelOverride, concurrentUpdatesByDefaultOverride, ); root.current = uninitializedFiber; diff --git a/packages/react-reconciler/src/ReactFiberRoot.old.js b/packages/react-reconciler/src/ReactFiberRoot.old.js index 68d329492cf1b..83d4de95b6a8b 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.old.js +++ b/packages/react-reconciler/src/ReactFiberRoot.old.js @@ -98,7 +98,6 @@ export function createFiberRoot( tag: RootTag, hydrate: boolean, hydrationCallbacks: null | SuspenseHydrationCallbacks, - strictModeLevelOverride: null | number, concurrentUpdatesByDefaultOverride: null | boolean, ): FiberRoot { const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any); @@ -110,7 +109,6 @@ export function createFiberRoot( // stateNode is any. const uninitializedFiber = createHostRootFiber( tag, - strictModeLevelOverride, concurrentUpdatesByDefaultOverride, ); root.current = uninitializedFiber; diff --git a/packages/react-reconciler/src/__tests__/DebugTracing-test.internal.js b/packages/react-reconciler/src/__tests__/DebugTracing-test.internal.js index 83fc6b2eba890..c3dc4df1cf6ff 100644 --- a/packages/react-reconciler/src/__tests__/DebugTracing-test.internal.js +++ b/packages/react-reconciler/src/__tests__/DebugTracing-test.internal.js @@ -282,7 +282,6 @@ describe('DebugTracing', () => { expect(logs).toEqual([ `group: ⚛️ render (${DEFAULT_LANE_STRING})`, `log: ⚛️ Example updated state (${DEFAULT_LANE_STRING})`, - `log: ⚛️ Example updated state (${DEFAULT_LANE_STRING})`, `groupEnd: ⚛️ render (${DEFAULT_LANE_STRING})`, ]); }); @@ -366,7 +365,6 @@ describe('DebugTracing', () => { expect(logs).toEqual([ `group: ⚛️ render (${DEFAULT_LANE_STRING})`, `log: ⚛️ Example updated state (${DEFAULT_LANE_STRING})`, - `log: ⚛️ Example updated state (${DEFAULT_LANE_STRING})`, // debugRenderPhaseSideEffectsForStrictMode `groupEnd: ⚛️ render (${DEFAULT_LANE_STRING})`, ]); }); diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js index 27d09697fc213..6758d9d4dc178 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js @@ -520,11 +520,7 @@ describe('ReactHooksWithNoopRenderer', () => { , ); expect(() => - expect(Scheduler).toFlushAndYield( - __DEV__ - ? ['Foo [0]', 'Bar', 'Foo [2]'] - : ['Foo [0]', 'Bar', 'Foo [1]'], - ), + expect(Scheduler).toFlushAndYield(['Foo [0]', 'Bar', 'Foo [1]']), ).toErrorDev([ 'Cannot update a component (`Foo`) while rendering a ' + 'different component (`Bar`). To locate the bad setState() call inside `Bar`', @@ -539,11 +535,7 @@ describe('ReactHooksWithNoopRenderer', () => { , ); - expect(Scheduler).toFlushAndYield( - __DEV__ - ? ['Foo [2]', 'Bar', 'Foo [4]'] - : ['Foo [1]', 'Bar', 'Foo [2]'], - ); + expect(Scheduler).toFlushAndYield(['Foo [1]', 'Bar', 'Foo [2]']); }); }); @@ -1765,16 +1757,11 @@ describe('ReactHooksWithNoopRenderer', () => { return ; } - // we explicitly wait for missing act() warnings here since - // it's a lot harder to simulate this condition inside an act scope - expect(() => { - ReactNoop.render(, () => - Scheduler.unstable_yieldValue('Sync effect'), - ); - expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']); - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]); - }).toErrorDev(['An update to Counter ran an effect']); - + ReactNoop.render(, () => + Scheduler.unstable_yieldValue('Sync effect'), + ); + expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']); + expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]); // A flush sync doesn't cause the passive effects to fire. // So we haven't added the other update yet. act(() => { diff --git a/packages/react-reconciler/src/__tests__/ReactIncremental-test.js b/packages/react-reconciler/src/__tests__/ReactIncremental-test.js index e505d1e243fa4..efcb4d7288875 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncremental-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncremental-test.js @@ -1863,18 +1863,11 @@ describe('ReactIncremental', () => { , ); - expect(() => - expect(Scheduler).toFlushAndYield([ - 'Intl {}', - 'ShowLocale {"locale":"fr"}', - 'ShowBoth {"locale":"fr"}', - ]), - ).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: Intl, ShowBoth, ShowLocale', - ); + expect(Scheduler).toFlushAndYield([ + 'Intl {}', + 'ShowLocale {"locale":"fr"}', + 'ShowBoth {"locale":"fr"}', + ]); ReactNoop.render( @@ -1921,28 +1914,21 @@ describe('ReactIncremental', () => { , ); - expect(() => - expect(Scheduler).toFlushAndYield([ - 'ShowLocale {"locale":"sv"}', - 'ShowBoth {"locale":"sv"}', - 'Intl {}', - 'ShowLocale {"locale":"en"}', - 'Router {}', - 'Indirection {}', - 'ShowLocale {"locale":"en"}', - 'ShowRoute {"route":"/about"}', - 'ShowNeither {}', - 'Intl {}', - 'ShowBoth {"locale":"ru","route":"/about"}', - 'ShowBoth {"locale":"en","route":"/about"}', - 'ShowBoth {"locale":"en"}', - ]), - ).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: Router, ShowRoute', - ); + expect(Scheduler).toFlushAndYield([ + 'ShowLocale {"locale":"sv"}', + 'ShowBoth {"locale":"sv"}', + 'Intl {}', + 'ShowLocale {"locale":"en"}', + 'Router {}', + 'Indirection {}', + 'ShowLocale {"locale":"en"}', + 'ShowRoute {"route":"/about"}', + 'ShowNeither {}', + 'Intl {}', + 'ShowBoth {"locale":"ru","route":"/about"}', + 'ShowBoth {"locale":"en","route":"/about"}', + 'ShowBoth {"locale":"en"}', + ]); }); it('does not leak own context into context provider', () => { @@ -1968,19 +1954,12 @@ describe('ReactIncremental', () => { } ReactNoop.render(); - expect(() => - expect(Scheduler).toFlushAndYield([ - 'Recurse {}', - 'Recurse {"n":2}', - 'Recurse {"n":1}', - 'Recurse {"n":0}', - ]), - ).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: Recurse', - ); + expect(Scheduler).toFlushAndYield([ + 'Recurse {}', + 'Recurse {"n":2}', + 'Recurse {"n":1}', + 'Recurse {"n":0}', + ]); }); if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) { @@ -2020,10 +1999,6 @@ describe('ReactIncremental', () => { "If you can't use a class try assigning the prototype on the function as a workaround. " + '`Recurse.prototype = React.Component.prototype`. ' + "Don't use an arrow function since it cannot be called with `new` by React.", - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: Recurse', ]); }); } @@ -2092,18 +2067,11 @@ describe('ReactIncremental', () => { 'ShowLocale {"locale":"fr"}', ]); - expect(() => - expect(Scheduler).toFlushAndYield([ - 'ShowLocale {"locale":"fr"}', - 'Intl {}', - 'ShowLocale {"locale":"ru"}', - ]), - ).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: Intl, ShowLocale', - ); + expect(Scheduler).toFlushAndYield([ + 'ShowLocale {"locale":"fr"}', + 'Intl {}', + 'ShowLocale {"locale":"ru"}', + ]); }); it('reads context when setState is below the provider', () => { @@ -2186,21 +2154,14 @@ describe('ReactIncremental', () => { , ); - expect(() => - expect(Scheduler).toFlushAndYield([ - 'Intl:read {}', - 'Intl:provide {"locale":"fr"}', - 'IndirectionFn {}', - 'IndirectionClass {}', - 'ShowLocaleClass:read {"locale":"fr"}', - 'ShowLocaleFn:read {"locale":"fr"}', - ]), - ).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: Intl, ShowLocaleClass, ShowLocaleFn', - ); + expect(Scheduler).toFlushAndYield([ + 'Intl:read {}', + 'Intl:provide {"locale":"fr"}', + 'IndirectionFn {}', + 'IndirectionClass {}', + 'ShowLocaleClass:read {"locale":"fr"}', + 'ShowLocaleFn:read {"locale":"fr"}', + ]); statefulInst.setState({x: 1}); expect(Scheduler).toFlushWithoutYielding(); @@ -2287,21 +2248,14 @@ describe('ReactIncremental', () => { , ); - expect(() => - expect(Scheduler).toFlushAndYield([ - 'Intl:read {}', - 'Intl:provide {"locale":"fr"}', - 'IndirectionFn {}', - 'IndirectionClass {}', - 'ShowLocaleClass:read {"locale":"fr"}', - 'ShowLocaleFn:read {"locale":"fr"}', - ]), - ).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: Intl, ShowLocaleClass, ShowLocaleFn', - ); + expect(Scheduler).toFlushAndYield([ + 'Intl:read {}', + 'Intl:provide {"locale":"fr"}', + 'IndirectionFn {}', + 'IndirectionClass {}', + 'ShowLocaleClass:read {"locale":"fr"}', + 'ShowLocaleFn:read {"locale":"fr"}', + ]); statefulInst.setState({locale: 'gr'}); expect(Scheduler).toFlushAndYield([ @@ -2356,12 +2310,7 @@ describe('ReactIncremental', () => { // Init ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: Child', - ); + expect(Scheduler).toFlushWithoutYielding(); // Trigger an update in the middle of the tree instance.setState({}); @@ -2407,12 +2356,7 @@ describe('ReactIncremental', () => { // Init ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: ContextProvider', - ); + expect(Scheduler).toFlushWithoutYielding(); // Trigger an update in the middle of the tree // This is necessary to reproduce the error as it currently exists. @@ -2454,27 +2398,16 @@ describe('ReactIncremental', () => { } ReactNoop.render(); - expect(() => - expect(Scheduler).toFlushAndYield([ - 'render', - 'componentDidMount', - 'shouldComponentUpdate', - 'render', - 'componentDidUpdate', - 'shouldComponentUpdate', - 'render', - 'componentDidUpdate', - ]), - ).toErrorDev( - [ - 'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended', - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: MyComponent', - ], - {withoutStack: 1}, - ); + expect(Scheduler).toFlushAndYield([ + 'render', + 'componentDidMount', + 'shouldComponentUpdate', + 'render', + 'componentDidUpdate', + 'shouldComponentUpdate', + 'render', + 'componentDidUpdate', + ]); }); xit('should reuse memoized work if pointers are updated before calling lifecycles', () => { @@ -2604,12 +2537,7 @@ describe('ReactIncremental', () => { , ); - expect(() => expect(Scheduler).toFlushAndYield(['count:0'])).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: Child, TopContextProvider', - ); + expect(Scheduler).toFlushAndYield(['count:0']); instance.updateCount(); expect(Scheduler).toFlushAndYield(['count:1']); }); @@ -2664,12 +2592,7 @@ describe('ReactIncremental', () => { , ); - expect(() => expect(Scheduler).toFlushAndYield(['count:0'])).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: Child, MiddleContextProvider, TopContextProvider', - ); + expect(Scheduler).toFlushAndYield(['count:0']); instance.updateCount(); expect(Scheduler).toFlushAndYield(['count:1']); }); @@ -2733,12 +2656,7 @@ describe('ReactIncremental', () => { , ); - expect(() => expect(Scheduler).toFlushAndYield(['count:0'])).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: Child, MiddleContextProvider, TopContextProvider', - ); + expect(Scheduler).toFlushAndYield(['count:0']); instance.updateCount(); expect(Scheduler).toFlushWithoutYielding(); }); @@ -2814,14 +2732,7 @@ describe('ReactIncremental', () => { , ); - expect(() => - expect(Scheduler).toFlushAndYield(['count:0, name:brian']), - ).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: Child, MiddleContextProvider, TopContextProvider', - ); + expect(Scheduler).toFlushAndYield(['count:0, name:brian']); topInstance.updateCount(); expect(Scheduler).toFlushWithoutYielding(); middleInstance.updateName('not brian'); @@ -2933,7 +2844,11 @@ describe('ReactIncremental', () => { return this.state.didError ? null : ; } } - ReactNoop.render(); + ReactNoop.render( + + + , + ); expect(() => { expect(Scheduler).toFlushWithoutYielding(); }).toErrorDev([ diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js index 186085c843025..d26a433b6fa58 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js @@ -1286,12 +1286,7 @@ describe('ReactIncrementalErrorHandling', () => { , ); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but ' + - 'applications using it should migrate to the new version.\n\n' + - 'Please update the following components: Connector, Provider', - ); + expect(Scheduler).toFlushWithoutYielding(); // If the context stack does not unwind, span will get 'abcde' expect(ReactNoop.getChildren()).toEqual([span('a')]); @@ -1843,10 +1838,6 @@ describe('ReactIncrementalErrorHandling', () => { "If you can't use a class try assigning the prototype on the function as a workaround. " + '`Provider.prototype = React.Component.prototype`. ' + "Don't use an arrow function since it cannot be called with `new` by React.", - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but ' + - 'applications using it should migrate to the new version.\n\n' + - 'Please update the following components: Provider', ]); }); } diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.js index b372bc8fce25a..def901c153a9e 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.js @@ -78,12 +78,7 @@ describe('ReactIncrementalReflection', () => { expect(instances[0]._isMounted()).toBe(false); // Render the rest and commit the updates. - expect(() => - expect(Scheduler).toFlushAndYield(['componentDidMount: true']), - ).toErrorDev( - 'Using UNSAFE_componentWillMount in strict mode is not recommended', - {withoutStack: true}, - ); + expect(Scheduler).toFlushAndYield(['componentDidMount: true']); expect(instances[0]._isMounted()).toBe(true); }); @@ -120,12 +115,7 @@ describe('ReactIncrementalReflection', () => { } ReactNoop.render(); - expect(() => - expect(Scheduler).toFlushAndYield(['Component']), - ).toErrorDev( - 'Using UNSAFE_componentWillMount in strict mode is not recommended', - {withoutStack: true}, - ); + expect(Scheduler).toFlushAndYield(['Component']); expect(instances[0]._isMounted()).toBe(true); @@ -238,15 +228,7 @@ describe('ReactIncrementalReflection', () => { // not find any host nodes in it. expect(findInstance(classInstance)).toBe(null); - expect(() => - expect(Scheduler).toFlushAndYield([['componentDidMount', span()]]), - ).toErrorDev( - [ - 'Using UNSAFE_componentWillMount in strict mode is not recommended', - 'Using UNSAFE_componentWillUpdate in strict mode is not recommended', - ], - {withoutStack: true}, - ); + expect(Scheduler).toFlushAndYield([['componentDidMount', span()]]); const hostSpan = classInstance.span; expect(hostSpan).toBeDefined(); diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js index 32d4699318a17..fd819eeb32b46 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js @@ -1308,9 +1308,7 @@ describe('ReactIncrementalSideEffects', () => { } ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( - 'Warning: A string ref, "bar", has been found within a strict mode tree.', - ); + expect(Scheduler).toFlushWithoutYielding(); expect(fooInstance.refs.bar.test).toEqual('test'); }); diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js index 991937ee37fa0..a3fcf9dc7011d 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js @@ -402,12 +402,7 @@ describe('ReactIncrementalUpdates', () => { } } ReactNoop.render(); - expect(() => - expect(Scheduler).toFlushAndYield(['render']), - ).toErrorDev( - 'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended', - {withoutStack: true}, - ); + expect(Scheduler).toFlushAndYield(['render']); ReactNoop.flushSync(() => { instance.setState({a: 'a'}); diff --git a/packages/react-reconciler/src/__tests__/ReactNewContext-test.js b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js index 5c338f01db8e5..f81a2131e0b9b 100644 --- a/packages/react-reconciler/src/__tests__/ReactNewContext-test.js +++ b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js @@ -953,14 +953,7 @@ describe('ReactNewContext', () => { , ); - expect(() => { - expect(Scheduler).toFlushAndYield(['LegacyProvider', 'App', 'Child']); - }).toErrorDev( - 'Legacy context API has been detected within a strict-mode tree.\n\n' + - 'The old API will be supported in all 16.x releases, but applications ' + - 'using it should migrate to the new version.\n\n' + - 'Please update the following components: LegacyProvider', - ); + expect(Scheduler).toFlushAndYield(['LegacyProvider', 'App', 'Child']); expect(ReactNoop.getChildren()).toEqual([span('Child')]); // Update App with same value (should bail out) @@ -1244,8 +1237,6 @@ describe('ReactNewContext', () => { ReactNoop.render(); expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev([ - 'Context can only be read while React is rendering', - // A second warning comes from to setStates being added to the queue. 'Context can only be read while React is rendering', 'Cannot update during an existing state transition', ]); diff --git a/packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js b/packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js index fbd81d9576494..5cc884d57fa59 100644 --- a/packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js +++ b/packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js @@ -1836,9 +1836,14 @@ describe('useMutableSource', () => { return null; } + // TODO The mechanism for this type of detection relies on StrictMode double rendering. expect(() => { act(() => { - ReactNoop.render(); + ReactNoop.render( + + + , + ); }); }).toThrow( 'A mutable source was mutated while the MutateDuringRead component ' + diff --git a/packages/react-test-renderer/src/ReactTestRenderer.js b/packages/react-test-renderer/src/ReactTestRenderer.js index 7c64a16936005..bc46f1d6d182f 100644 --- a/packages/react-test-renderer/src/ReactTestRenderer.js +++ b/packages/react-test-renderer/src/ReactTestRenderer.js @@ -58,7 +58,6 @@ const {IsSomeRendererActing} = ReactSharedInternals; type TestRendererOptions = { createNodeMock: (element: React$Element) => any, unstable_isConcurrent: boolean, - unstable_strictModeLevel: number, unstable_concurrentUpdatesByDefault: boolean, ... }; @@ -437,7 +436,6 @@ function propsMatch(props: Object, filter: Object): boolean { function create(element: React$Element, options: TestRendererOptions) { let createNodeMock = defaultTestOptions.createNodeMock; let isConcurrent = false; - let strictModeLevel = null; let concurrentUpdatesByDefault = null; if (typeof options === 'object' && options !== null) { if (typeof options.createNodeMock === 'function') { @@ -446,9 +444,6 @@ function create(element: React$Element, options: TestRendererOptions) { if (options.unstable_isConcurrent === true) { isConcurrent = true; } - if (options.unstable_strictModeLevel !== undefined) { - strictModeLevel = options.unstable_strictModeLevel; - } if (allowConcurrentByDefault) { if (options.unstable_concurrentUpdatesByDefault !== undefined) { concurrentUpdatesByDefault = @@ -466,7 +461,6 @@ function create(element: React$Element, options: TestRendererOptions) { isConcurrent ? ConcurrentRoot : LegacyRoot, false, null, - strictModeLevel, concurrentUpdatesByDefault, ); invariant(root != null, 'something went wrong'); diff --git a/packages/react/src/__tests__/ReactStrictMode-test.internal.js b/packages/react/src/__tests__/ReactStrictMode-test.internal.js index 20c83a46dfaac..351ce098ef29b 100644 --- a/packages/react/src/__tests__/ReactStrictMode-test.internal.js +++ b/packages/react/src/__tests__/ReactStrictMode-test.internal.js @@ -50,12 +50,10 @@ describe('ReactStrictMode', () => { } // @gate experimental - it('should support overriding default via createRoot option', () => { + it('should default to not strict', () => { act(() => { const container = document.createElement('div'); - const root = ReactDOM.createRoot(container, { - unstable_strictModeLevel: 0, - }); + const root = ReactDOM.createRoot(container); root.render(); }); @@ -66,27 +64,6 @@ describe('ReactStrictMode', () => { ]); }); - // @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)', () => { @@ -156,9 +133,7 @@ describe('ReactStrictMode', () => { it('should allow level to be increased with nesting', () => { act(() => { const container = document.createElement('div'); - const root = ReactDOM.createRoot(container, { - unstable_strictModeLevel: 0, - }); + const root = ReactDOM.createRoot(container); root.render( <> @@ -197,9 +172,7 @@ describe('ReactStrictMode', () => { it('should not allow level to be decreased with nesting', () => { act(() => { const container = document.createElement('div'); - const root = ReactDOM.createRoot(container, { - unstable_strictModeLevel: 2, - }); + const root = ReactDOM.createRoot(container); root.render( <> @@ -216,7 +189,6 @@ describe('ReactStrictMode', () => { }); expect(log).toEqual([ - 'A: render', 'A: render', 'B: render', 'B: render', @@ -228,18 +200,6 @@ describe('ReactStrictMode', () => { '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', ]); }); } diff --git a/packages/react/src/__tests__/ReactStrictMode-test.js b/packages/react/src/__tests__/ReactStrictMode-test.js index 60862d12a9fe8..c6d1f7ee72e6e 100644 --- a/packages/react/src/__tests__/ReactStrictMode-test.js +++ b/packages/react/src/__tests__/ReactStrictMode-test.js @@ -363,8 +363,15 @@ describe('Concurrent Mode', () => { }); // @gate experimental - it('should warn about unsafe legacy lifecycle methods anywhere in the tree', () => { - class AsyncRoot extends React.Component { + it('should warn about unsafe legacy lifecycle methods anywhere in a StrictMode tree', () => { + function StrictRoot() { + return ( + + + + ); + } + class App extends React.Component { UNSAFE_componentWillMount() {} UNSAFE_componentWillUpdate() {} render() { @@ -399,7 +406,7 @@ describe('Concurrent Mode', () => { const container = document.createElement('div'); const root = ReactDOM.unstable_createRoot(container); - root.render(); + root.render(); expect(() => Scheduler.unstable_flushAll()).toErrorDev( [ /* eslint-disable max-len */ @@ -407,7 +414,7 @@ describe('Concurrent Mode', () => { * Move code with side effects to componentDidMount, and set initial state in the constructor. -Please update the following components: AsyncRoot`, +Please update the following components: App`, `Warning: Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://reactjs.org/link/unsafe-component-lifecycles for details. * Move data fetching code or side effects to componentDidUpdate. @@ -418,20 +425,27 @@ Please update the following components: Bar, Foo`, * Move data fetching code or side effects to componentDidUpdate. -Please update the following components: AsyncRoot`, +Please update the following components: App`, /* eslint-enable max-len */ ], {withoutStack: true}, ); // Dedupe - root.render(); + root.render(); Scheduler.unstable_flushAll(); }); // @gate experimental it('should coalesce warnings by lifecycle name', () => { - class AsyncRoot extends React.Component { + function StrictRoot() { + return ( + + + + ); + } + class App extends React.Component { UNSAFE_componentWillMount() {} UNSAFE_componentWillUpdate() {} render() { @@ -455,7 +469,7 @@ Please update the following components: AsyncRoot`, const container = document.createElement('div'); const root = ReactDOM.unstable_createRoot(container); - root.render(); + root.render(); expect(() => { expect(() => Scheduler.unstable_flushAll()).toErrorDev( @@ -465,7 +479,7 @@ Please update the following components: AsyncRoot`, * Move code with side effects to componentDidMount, and set initial state in the constructor. -Please update the following components: AsyncRoot`, +Please update the following components: App`, `Warning: Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://reactjs.org/link/unsafe-component-lifecycles for details. * Move data fetching code or side effects to componentDidUpdate. @@ -476,7 +490,7 @@ Please update the following components: Child`, * Move data fetching code or side effects to componentDidUpdate. -Please update the following components: AsyncRoot`, +Please update the following components: App`, /* eslint-enable max-len */ ], {withoutStack: true}, @@ -508,16 +522,14 @@ Please update the following components: Parent`, {withoutStack: true}, ); // Dedupe - root.render(); + root.render(); Scheduler.unstable_flushAll(); }); // @gate experimental it('should warn about components not present during the initial render', () => { - class AsyncRoot extends React.Component { - render() { - return this.props.foo ? : ; - } + function StrictRoot({foo}) { + return {foo ? : }; } class Foo extends React.Component { UNSAFE_componentWillMount() {} @@ -534,7 +546,7 @@ Please update the following components: Parent`, const container = document.createElement('div'); const root = ReactDOM.unstable_createRoot(container); - root.render(); + root.render(); expect(() => Scheduler.unstable_flushAll(), ).toErrorDev( @@ -542,7 +554,7 @@ Please update the following components: Parent`, {withoutStack: true}, ); - root.render(); + root.render(); expect(() => Scheduler.unstable_flushAll(), ).toErrorDev( @@ -551,9 +563,9 @@ Please update the following components: Parent`, ); // Dedupe - root.render(); + root.render(); Scheduler.unstable_flushAll(); - root.render(); + root.render(); Scheduler.unstable_flushAll(); }); diff --git a/packages/react/src/__tests__/forwardRef-test.js b/packages/react/src/__tests__/forwardRef-test.js index 9c594082418ac..4dfb4a3f9cd88 100644 --- a/packages/react/src/__tests__/forwardRef-test.js +++ b/packages/react/src/__tests__/forwardRef-test.js @@ -287,11 +287,11 @@ describe('forwardRef', () => { ReactNoop.render(); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 2 : 1); + expect(renderCount).toBe(1); ReactNoop.render(); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 4 : 2); + expect(renderCount).toBe(2); }); it('should bailout if forwardRef is wrapped in memo', () => { @@ -310,13 +310,13 @@ describe('forwardRef', () => { ReactNoop.render(); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 2 : 1); + expect(renderCount).toBe(1); expect(ref.current.type).toBe('div'); ReactNoop.render(); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 2 : 1); + expect(renderCount).toBe(1); const differentRef = React.createRef(); @@ -324,14 +324,14 @@ describe('forwardRef', () => { , ); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 4 : 2); + expect(renderCount).toBe(2); expect(ref.current).toBe(null); expect(differentRef.current.type).toBe('div'); ReactNoop.render(); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 6 : 3); + expect(renderCount).toBe(3); }); it('should custom memo comparisons to compose', () => { @@ -351,19 +351,19 @@ describe('forwardRef', () => { ReactNoop.render(); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 2 : 1); + expect(renderCount).toBe(1); expect(ref.current.type).toBe('div'); // Changing either a or b rerenders ReactNoop.render(); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 4 : 2); + expect(renderCount).toBe(2); // Changing c doesn't rerender ReactNoop.render(); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 4 : 2); + expect(renderCount).toBe(2); const ComposedMemo = React.memo( RefForwardingComponent, @@ -372,29 +372,29 @@ describe('forwardRef', () => { ReactNoop.render(); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 6 : 3); + expect(renderCount).toBe(3); // Changing just b no longer updates ReactNoop.render(); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 6 : 3); + expect(renderCount).toBe(3); // Changing just a and c updates ReactNoop.render(); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 8 : 4); + expect(renderCount).toBe(4); // Changing just c does not update ReactNoop.render(); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 8 : 4); + expect(renderCount).toBe(4); // Changing ref still rerenders const differentRef = React.createRef(); ReactNoop.render(); expect(Scheduler).toFlushWithoutYielding(); - expect(renderCount).toBe(__DEV__ ? 10 : 5); + expect(renderCount).toBe(5); expect(ref.current).toBe(null); expect(differentRef.current.type).toBe('div');