From e7021b848ecb8a50d94f77af79779126e490368b Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Wed, 27 Mar 2024 01:38:57 -0400 Subject: [PATCH] Update ART to concurrent, RN and Noop to error --- packages/react-art/src/ReactART.js | 17 +++++++++---- .../react-native-renderer/src/ReactFabric.js | 5 ++++ .../src/ReactNativeRenderer.js | 6 +++++ .../ReactNativeError-test.internal.js | 1 + .../ReactNativeEvents-test.internal.js | 7 ++++++ .../ReactNativeMount-test.internal.js | 24 ++++++++++++++++++ ...ReactNativeComponentClass-test.internal.js | 1 + .../src/createReactNoop.js | 9 ++++++- .../src/__tests__/Activity-test.js | 2 +- .../__tests__/DebugTracing-test.internal.js | 4 +-- .../__tests__/ReactContextPropagation-test.js | 2 +- .../ReactHooksWithNoopRenderer-test.js | 1 + .../src/__tests__/ReactIsomorphicAct-test.js | 6 ++--- .../ReactSubtreeFlagsWarning-test.js | 2 +- .../ReactSuspenseEffectsSemantics-test.js | 6 ++--- .../ReactSuspenseFuzz-test.internal.js | 17 +++++++++---- .../src/__tests__/ReactSuspenseList-test.js | 2 +- .../ReactSuspensePlaceholder-test.internal.js | 2 ++ .../ReactSuspenseWithNoopRenderer-test.js | 25 ++++++++++--------- .../src/__tests__/StrictEffectsMode-test.js | 2 ++ ...StrictEffectsModeDefaults-test.internal.js | 2 ++ 21 files changed, 108 insertions(+), 35 deletions(-) diff --git a/packages/react-art/src/ReactART.js b/packages/react-art/src/ReactART.js index 9b065a2b1185c..6fbbbc83a196e 100644 --- a/packages/react-art/src/ReactART.js +++ b/packages/react-art/src/ReactART.js @@ -7,11 +7,12 @@ import * as React from 'react'; import ReactVersion from 'shared/ReactVersion'; -import {LegacyRoot} from 'react-reconciler/src/ReactRootTags'; +import {ConcurrentRoot} from 'react-reconciler/src/ReactRootTags'; import { createContainer, updateContainer, injectIntoDevTools, + flushSync, } from 'react-reconciler/src/ReactFiberReconciler'; import Transform from 'art/core/transform'; import Mode from 'art/modes/current'; @@ -68,13 +69,15 @@ class Surface extends React.Component { this._mountNode = createContainer( this._surface, - LegacyRoot, + ConcurrentRoot, null, false, false, '', ); - updateContainer(this.props.children, this._mountNode, this); + flushSync(() => { + updateContainer(this.props.children, this._mountNode, this); + }); } componentDidUpdate(prevProps, prevState) { @@ -84,7 +87,9 @@ class Surface extends React.Component { this._surface.resize(+props.width, +props.height); } - updateContainer(this.props.children, this._mountNode, this); + flushSync(() => { + updateContainer(this.props.children, this._mountNode, this); + }); if (this._surface.render) { this._surface.render(); @@ -92,7 +97,9 @@ class Surface extends React.Component { } componentWillUnmount() { - updateContainer(null, this._mountNode, this); + flushSync(() => { + updateContainer(null, this._mountNode, this); + }); } render() { diff --git a/packages/react-native-renderer/src/ReactFabric.js b/packages/react-native-renderer/src/ReactFabric.js index 6cdb179093b12..2f534013aa2e6 100644 --- a/packages/react-native-renderer/src/ReactFabric.js +++ b/packages/react-native-renderer/src/ReactFabric.js @@ -48,6 +48,7 @@ import {getPublicInstanceFromInternalInstanceHandle} from './ReactFiberConfigFab // Module provided by RN: import {ReactFiberErrorDialog} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; +import {disableLegacyMode} from 'shared/ReactFeatureFlags'; if (typeof ReactFiberErrorDialog.showErrorDialog !== 'function') { throw new Error( @@ -106,6 +107,10 @@ function render( callback: ?() => void, concurrentRoot: ?boolean, ): ?ElementRef { + if (disableLegacyMode && !concurrentRoot) { + throw new Error('render: Unsupported Legacy Mode API.'); + } + let root = roots.get(containerTag); if (!root) { diff --git a/packages/react-native-renderer/src/ReactNativeRenderer.js b/packages/react-native-renderer/src/ReactNativeRenderer.js index 5409eb3df4f98..7c5410cfe4473 100644 --- a/packages/react-native-renderer/src/ReactNativeRenderer.js +++ b/packages/react-native-renderer/src/ReactNativeRenderer.js @@ -50,6 +50,8 @@ import { isChildPublicInstance, } from './ReactNativePublicCompat'; +import {disableLegacyMode} from 'shared/ReactFeatureFlags'; + // Module provided by RN: import {ReactFiberErrorDialog} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; @@ -109,6 +111,10 @@ function render( containerTag: number, callback: ?() => void, ): ?ElementRef { + if (disableLegacyMode) { + throw new Error('render: Unsupported Legacy Mode API.'); + } + let root = roots.get(containerTag); if (!root) { diff --git a/packages/react-native-renderer/src/__tests__/ReactNativeError-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactNativeError-test.internal.js index b056f7a80229a..a7ac05b214492 100644 --- a/packages/react-native-renderer/src/__tests__/ReactNativeError-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactNativeError-test.internal.js @@ -50,6 +50,7 @@ describe('ReactNativeError', () => { ); }); + // @gate !disableLegacyMode it('should be able to extract a component stack from a native view', () => { const View = createReactNativeComponentClass('View', () => ({ validAttributes: {foo: true}, diff --git a/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js index f210de5f8c456..43f1f4df563ce 100644 --- a/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js @@ -79,6 +79,7 @@ beforeEach(() => { .ReactNativeViewConfigRegistry.register; }); +// @gate !disableLegacyMode it('fails to register the same event name with different types', async () => { const InvalidEvents = createReactNativeComponentClass('InvalidEvents', () => { if (!__DEV__) { @@ -122,6 +123,7 @@ it('fails to register the same event name with different types', async () => { ).rejects.toThrow('Event cannot be both direct and bubbling: topChange'); }); +// @gate !disableLegacyMode it('fails if unknown/unsupported event types are dispatched', () => { expect(RCTEventEmitter.register).toHaveBeenCalledTimes(1); const EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; @@ -143,6 +145,7 @@ it('fails if unknown/unsupported event types are dispatched', () => { }).toThrow('Unsupported top level event type "unspecifiedEvent" dispatched'); }); +// @gate !disableLegacyMode it('handles events', () => { expect(RCTEventEmitter.register).toHaveBeenCalledTimes(1); const EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; @@ -200,6 +203,7 @@ it('handles events', () => { }); // @gate !disableLegacyContext || !__DEV__ +// @gate !disableLegacyMode it('handles events on text nodes', () => { expect(RCTEventEmitter.register).toHaveBeenCalledTimes(1); const EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; @@ -283,6 +287,7 @@ it('handles events on text nodes', () => { ]); }); +// @gate !disableLegacyMode it('handles when a responder is unmounted while a touch sequence is in progress', () => { const EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; const View = fakeRequireNativeComponent('View', {id: true}); @@ -372,6 +377,7 @@ it('handles when a responder is unmounted while a touch sequence is in progress' expect(log).toEqual(['two responder start']); }); +// @gate !disableLegacyMode it('handles events without target', () => { const EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; @@ -462,6 +468,7 @@ it('handles events without target', () => { ]); }); +// @gate !disableLegacyMode it('dispatches event with target as instance', () => { const EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; diff --git a/packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js index 06604f2d062f9..576c7175d2021 100644 --- a/packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js @@ -45,6 +45,7 @@ describe('ReactNative', () => { require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface').TextInputState; }); + // @gate !disableLegacyMode it('should be able to create and render a native component', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -58,6 +59,7 @@ describe('ReactNative', () => { expect(UIManager.updateView).not.toBeCalled(); }); + // @gate !disableLegacyMode it('should be able to create and update a native component', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -79,6 +81,7 @@ describe('ReactNative', () => { expect(UIManager.updateView).toBeCalledWith(3, 'RCTView', {foo: 'bar'}); }); + // @gate !disableLegacyMode it('should not call UIManager.updateView after render for properties that have not changed', () => { const Text = createReactNativeComponentClass('RCTText', () => ({ validAttributes: {foo: true}, @@ -105,6 +108,7 @@ describe('ReactNative', () => { expect(UIManager.updateView).toHaveBeenCalledTimes(4); }); + // @gate !disableLegacyMode it('should call dispatchCommand for native refs', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -133,6 +137,7 @@ describe('ReactNative', () => { ); }); + // @gate !disableLegacyMode it('should warn and no-op if calling dispatchCommand on non native refs', () => { class BasicClass extends React.Component { render() { @@ -162,6 +167,7 @@ describe('ReactNative', () => { expect(UIManager.dispatchViewManagerCommand).not.toBeCalled(); }); + // @gate !disableLegacyMode it('should call sendAccessibilityEvent for native refs', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -192,6 +198,7 @@ describe('ReactNative', () => { ).toHaveBeenCalledWith(expect.any(Number), 'focus'); }); + // @gate !disableLegacyMode it('should warn and no-op if calling sendAccessibilityEvent on non native refs', () => { class BasicClass extends React.Component { render() { @@ -221,6 +228,7 @@ describe('ReactNative', () => { expect(UIManager.sendAccessibilityEvent).not.toBeCalled(); }); + // @gate !disableLegacyMode it('should not call UIManager.updateView from ref.setNativeProps for properties that have not changed', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -254,6 +262,7 @@ describe('ReactNative', () => { ); }); + // @gate !disableLegacyMode it('should call UIManager.measure on ref.measure', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -280,6 +289,7 @@ describe('ReactNative', () => { expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100, 0, 0); }); + // @gate !disableLegacyMode it('should call UIManager.measureInWindow on ref.measureInWindow', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -306,6 +316,7 @@ describe('ReactNative', () => { expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100); }); + // @gate !disableLegacyMode it('should support reactTag in ref.measureLayout', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -346,6 +357,7 @@ describe('ReactNative', () => { expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100); }); + // @gate !disableLegacyMode it('should support ref in ref.measureLayout of host components', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -382,6 +394,7 @@ describe('ReactNative', () => { expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100); }); + // @gate !disableLegacyMode it('returns the correct instance and calls it in the callback', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -403,6 +416,7 @@ describe('ReactNative', () => { expect(a).toBe(c); }); + // @gate !disableLegacyMode it('renders and reorders children', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {title: true}, @@ -433,6 +447,7 @@ describe('ReactNative', () => { expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchSnapshot(); }); + // @gate !disableLegacyMode it('calls setState with no arguments', () => { let mockArgs; class Component extends React.Component { @@ -448,6 +463,7 @@ describe('ReactNative', () => { expect(mockArgs.length).toEqual(0); }); + // @gate !disableLegacyMode it('should not throw when is used inside of a ancestor', () => { const Image = createReactNativeComponentClass('RCTImage', () => ({ validAttributes: {}, @@ -478,6 +494,7 @@ describe('ReactNative', () => { ); }); + // @gate !disableLegacyMode it('should throw for text not inside of a ancestor', async () => { const ScrollView = createReactNativeComponentClass('RCTScrollView', () => ({ validAttributes: {}, @@ -512,6 +529,7 @@ describe('ReactNative', () => { ); }); + // @gate !disableLegacyMode it('should not throw for text inside of an indirect ancestor', () => { const Text = createReactNativeComponentClass('RCTText', () => ({ validAttributes: {}, @@ -528,6 +546,7 @@ describe('ReactNative', () => { ); }); + // @gate !disableLegacyMode it('findHostInstance_DEPRECATED should warn if used to find a host component inside StrictMode', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -564,6 +583,7 @@ describe('ReactNative', () => { expect(match).toBe(child); }); + // @gate !disableLegacyMode it('findHostInstance_DEPRECATED should warn if passed a component that is inside StrictMode', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -601,6 +621,7 @@ describe('ReactNative', () => { expect(match).toBe(child); }); + // @gate !disableLegacyMode it('findNodeHandle should warn if used to find a host component inside StrictMode', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -635,6 +656,7 @@ describe('ReactNative', () => { expect(match).toBe(child._nativeTag); }); + // @gate !disableLegacyMode it('findNodeHandle should warn if passed a component that is inside StrictMode', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -670,6 +692,7 @@ describe('ReactNative', () => { expect(match).toBe(child._nativeTag); }); + // @gate !disableLegacyMode it('blur on host component calls TextInputState', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, @@ -687,6 +710,7 @@ describe('ReactNative', () => { expect(TextInputState.blurTextInput).toHaveBeenCalledWith(viewRef.current); }); + // @gate !disableLegacyMode it('focus on host component calls TextInputState', () => { const View = createReactNativeComponentClass('RCTView', () => ({ validAttributes: {foo: true}, diff --git a/packages/react-native-renderer/src/__tests__/createReactNativeComponentClass-test.internal.js b/packages/react-native-renderer/src/__tests__/createReactNativeComponentClass-test.internal.js index 5bb4b1fba0655..82199fd79e641 100644 --- a/packages/react-native-renderer/src/__tests__/createReactNativeComponentClass-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/createReactNativeComponentClass-test.internal.js @@ -25,6 +25,7 @@ describe('createReactNativeComponentClass', () => { ReactNative = require('react-native-renderer'); }); + // @gate !disableLegacyMode it('should register viewConfigs', () => { const textViewConfig = { validAttributes: {}, diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index af4612f00ddee..03937ee02cfcb 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -32,7 +32,7 @@ import { ConcurrentRoot, LegacyRoot, } from 'react-reconciler/constants'; -import {enableRefAsProp} from 'shared/ReactFeatureFlags'; +import {enableRefAsProp, disableLegacyMode} from 'shared/ReactFeatureFlags'; type Container = { rootID: string, @@ -1020,6 +1020,10 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { }, createLegacyRoot() { + if (disableLegacyMode) { + throw new Error('createLegacyRoot: Unsupported Legacy Mode API.'); + } + const container = { rootID: '' + idCounter++, pendingChildren: [], @@ -1119,6 +1123,9 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { }, renderLegacySyncRoot(element: React$Element, callback: ?Function) { + if (disableLegacyMode) { + throw new Error('createLegacyRoot: Unsupported Legacy Mode API.'); + } const rootID = DEFAULT_ROOT_ID; const container = ReactNoop.getOrCreateRootContainer(rootID, LegacyRoot); const root = roots.get(container.rootID); diff --git a/packages/react-reconciler/src/__tests__/Activity-test.js b/packages/react-reconciler/src/__tests__/Activity-test.js index fc6a8b563d197..d37513e01eb44 100644 --- a/packages/react-reconciler/src/__tests__/Activity-test.js +++ b/packages/react-reconciler/src/__tests__/Activity-test.js @@ -118,7 +118,7 @@ describe('Activity', () => { ); }); - // @gate www + // @gate www && !disableLegacyMode it('does not defer in legacy mode', async () => { let setState; function Foo() { diff --git a/packages/react-reconciler/src/__tests__/DebugTracing-test.internal.js b/packages/react-reconciler/src/__tests__/DebugTracing-test.internal.js index e4e100fe2daf9..6dbbc4e1515db 100644 --- a/packages/react-reconciler/src/__tests__/DebugTracing-test.internal.js +++ b/packages/react-reconciler/src/__tests__/DebugTracing-test.internal.js @@ -76,7 +76,7 @@ describe('DebugTracing', () => { expect(logs).toEqual([]); }); - // @gate experimental && build === 'development' && enableDebugTracing + // @gate experimental && build === 'development' && enableDebugTracing && && !disableLegacyMode it('should log sync render with suspense, legacy', async () => { let resolveFakeSuspensePromise; let didResolve = false; @@ -116,7 +116,7 @@ describe('DebugTracing', () => { expect(logs).toEqual(['log: ⚛️ Example resolved']); }); - // @gate experimental && build === 'development' && enableDebugTracing && enableCPUSuspense + // @gate experimental && build === 'development' && enableDebugTracing && enableCPUSuspense && !disableLegacyMode it('should log sync render with CPU suspense, legacy', async () => { function Example() { console.log(''); diff --git a/packages/react-reconciler/src/__tests__/ReactContextPropagation-test.js b/packages/react-reconciler/src/__tests__/ReactContextPropagation-test.js index 3247e8758e079..20e102ed6f718 100644 --- a/packages/react-reconciler/src/__tests__/ReactContextPropagation-test.js +++ b/packages/react-reconciler/src/__tests__/ReactContextPropagation-test.js @@ -489,7 +489,7 @@ describe('ReactLazyContextPropagation', () => { expect(root).toMatchRenderedOutput('BBB'); }); - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode test('context is propagated across retries (legacy)', async () => { const root = ReactNoop.createLegacyRoot(); diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js index 45b223e8106a4..399aa8dcfbe6a 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js @@ -1660,6 +1660,7 @@ describe('ReactHooksWithNoopRenderer', () => { expect(ReactNoop).toMatchRenderedOutput(); }); + // @gate !disableLegacyMode it( 'in legacy mode, useEffect is deferred and updates finish synchronously ' + '(in a single batch)', diff --git a/packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js b/packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js index 6b1c284a7eb9d..7fbb3efc3bb62 100644 --- a/packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js @@ -108,7 +108,7 @@ describe('isomorphic act()', () => { expect(returnValue).toEqual('hi'); }); - // @gate __DEV__ + // @gate __DEV__ && !disableLegacyMode test('in legacy mode, updates are batched', () => { const root = ReactNoop.createLegacyRoot(); @@ -136,7 +136,7 @@ describe('isomorphic act()', () => { expect(root).toMatchRenderedOutput('C'); }); - // @gate __DEV__ + // @gate __DEV__ && !disableLegacyMode test('in legacy mode, in an async scope, updates are batched until the first `await`', async () => { const root = ReactNoop.createLegacyRoot(); @@ -167,7 +167,7 @@ describe('isomorphic act()', () => { }); }); - // @gate __DEV__ + // @gate __DEV__ && !disableLegacyMode test('in legacy mode, in an async scope, updates are batched until the first `await` (regression test: batchedUpdates)', async () => { const root = ReactNoop.createLegacyRoot(); diff --git a/packages/react-reconciler/src/__tests__/ReactSubtreeFlagsWarning-test.js b/packages/react-reconciler/src/__tests__/ReactSubtreeFlagsWarning-test.js index 49bde67837cdf..1063091cda55b 100644 --- a/packages/react-reconciler/src/__tests__/ReactSubtreeFlagsWarning-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSubtreeFlagsWarning-test.js @@ -130,7 +130,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { const resolveText = resolveMostRecentTextCache; - // @gate experimental || www + // @gate www && !disableLegacyMode it('regression: false positive for legacy suspense', async () => { // Wrapping in memo because regular function components go through the // mountIndeterminateComponent path, which acts like there's no `current` diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemantics-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemantics-test.js index 85a983a9161dd..e0137575164b0 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemantics-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemantics-test.js @@ -324,7 +324,7 @@ describe('ReactSuspenseEffectsSemantics', () => { expect(ReactNoop).toMatchRenderedOutput(null); }); - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('should not change behavior in sync', async () => { class ClassText extends React.Component { componentDidMount() { @@ -445,7 +445,7 @@ describe('ReactSuspenseEffectsSemantics', () => { }); describe('layout effects within a tree that re-suspends in an update', () => { - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('should not be destroyed or recreated in legacy roots', async () => { function App({children = null}) { Scheduler.log('App render'); @@ -2542,7 +2542,7 @@ describe('ReactSuspenseEffectsSemantics', () => { return null; } - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('should not be cleared within legacy roots', async () => { class ClassComponent extends React.Component { render() { diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseFuzz-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSuspenseFuzz-test.internal.js index d099b6d72ffb2..9e16297c5cd90 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseFuzz-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseFuzz-test.internal.js @@ -4,6 +4,7 @@ let ReactNoop; let Scheduler; let act; let Random; +let ReactFeatureFlags; const SEED = process.env.FUZZ_TEST_SEED || 'default'; const prettyFormatPkg = require('pretty-format'); @@ -26,6 +27,7 @@ describe('ReactSuspenseFuzz', () => { Scheduler = require('scheduler'); act = require('internal-test-utils').act; Random = require('random-seed'); + ReactFeatureFlags = require('shared/ReactFeatureFlags'); }); jest.setTimeout(20000); @@ -163,16 +165,21 @@ describe('ReactSuspenseFuzz', () => { resetCache(); // Do it again in legacy mode. - const legacyRootThatSuspends = ReactNoop.createLegacyRoot(); - await act(() => { - legacyRootThatSuspends.render(children); - }); + if (!ReactFeatureFlags.disableLegacyMode) { + const legacyRootThatSuspends = ReactNoop.createLegacyRoot(); + await act(() => { + legacyRootThatSuspends.render(children); + }); + + expect(legacyRootThatSuspends.getChildrenAsJSX()).toEqual( + expectedOutput, + ); + } // Now compare the final output. It should be the same. expect(concurrentRootThatSuspends.getChildrenAsJSX()).toEqual( expectedOutput, ); - expect(legacyRootThatSuspends.getChildrenAsJSX()).toEqual(expectedOutput); // TODO: There are Scheduler logs in this test file but they were only // added for debugging purposes; we don't make any assertions on them. diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.js index 432546c683f5b..e69d7e4123425 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.js @@ -272,7 +272,7 @@ describe('ReactSuspenseList', () => { ); }); - // @gate enableSuspenseList + // @gate enableSuspenseList && !disableLegacyMode it('shows content independently in legacy mode regardless of option', async () => { const A = createAsyncText('A'); const B = createAsyncText('B'); diff --git a/packages/react-reconciler/src/__tests__/ReactSuspensePlaceholder-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSuspensePlaceholder-test.internal.js index a1771263fbd7c..01e2323df95e7 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspensePlaceholder-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspensePlaceholder-test.internal.js @@ -296,6 +296,7 @@ describe('ReactSuspensePlaceholder', () => { }); describe('when suspending during mount', () => { + // @gate !disableLegacyMode && !disableLegacyMode it('properly accounts for base durations when a suspended times out in a legacy tree', async () => { ReactNoop.renderLegacySyncRoot(); assertLog([ @@ -370,6 +371,7 @@ describe('ReactSuspensePlaceholder', () => { }); describe('when suspending during update', () => { + // @gate !disableLegacyMode && !disableLegacyMode it('properly accounts for base durations when a suspended times out in a legacy tree', async () => { ReactNoop.renderLegacySyncRoot( , diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js index 2d75f2bda18b0..fd37cdac69934 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js @@ -886,7 +886,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { }).not.toThrow(); }); - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('in legacy mode, errors when an update suspends without a Suspense boundary during a sync update', async () => { const root = ReactNoop.createLegacyRoot(); await expect(async () => { @@ -1032,7 +1032,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { }); describe('legacy mode mode', () => { - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('times out immediately', async () => { function App() { return ( @@ -1055,7 +1055,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { expect(ReactNoop).toMatchRenderedOutput(); }); - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('times out immediately when Suspense is in legacy mode', async () => { class UpdatingText extends React.Component { state = {step: 1}; @@ -1129,7 +1129,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { ); }); - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('does not re-render siblings in loose mode', async () => { class TextWithLifecycle extends React.Component { componentDidMount() { @@ -1204,7 +1204,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { ); }); - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('suspends inside constructor', async () => { class AsyncTextInConstructor extends React.Component { constructor(props) { @@ -1240,7 +1240,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { expect(ReactNoop).toMatchRenderedOutput(); }); - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('does not infinite loop if fallback contains lifecycle method', async () => { class Fallback extends React.Component { state = { @@ -1283,7 +1283,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { }); if (global.__PERSISTENT__) { - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('hides/unhides suspended children before layout effects fire (persistent)', async () => { const {useRef, useLayoutEffect} = React; @@ -1327,7 +1327,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { assertLog(['Hi']); }); } else { - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('hides/unhides suspended children before layout effects fire (mutation)', async () => { const {useRef, useLayoutEffect} = React; @@ -1370,7 +1370,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { }); } - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('handles errors in the return path of a component that suspends', async () => { // Covers an edge case where an error is thrown inside the complete phase // of a component that is in the return path of a component that suspends. @@ -1405,6 +1405,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { ); }); + // @gate !disableLegacyMode it('does not drop mounted effects', async () => { const never = {then() {}}; @@ -1455,7 +1456,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { }); }); - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('does not call lifecycles of a suspended component', async () => { class TextWithLifecycle extends React.Component { componentDidMount() { @@ -1523,7 +1524,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { ); }); - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('does not call lifecycles of a suspended component (hooks)', async () => { function TextWithLifecycle(props) { React.useLayoutEffect(() => { @@ -3885,7 +3886,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { assertLog(['Unmount Child']); }); - // @gate enableLegacyCache + // @gate enableLegacyCache && !disableLegacyMode it('should fire effect clean-up when deleting suspended tree (legacy)', async () => { const {useEffect} = React; diff --git a/packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js b/packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js index f673bf01c2cc0..34ee0b1851613 100644 --- a/packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js +++ b/packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js @@ -27,6 +27,7 @@ describe('StrictEffectsMode', () => { ReactNoop = require('react-noop-renderer'); }); + // @gate !disableLegacyMode it('should not double invoke effects in legacy mode', async () => { function App({text}) { React.useEffect(() => { @@ -430,6 +431,7 @@ describe('StrictEffectsMode', () => { assertLog(['componentWillUnmount']); }); + // @gate !disableLegacyMode it('should not double invoke class lifecycles in legacy mode', async () => { class App extends React.PureComponent { componentDidMount() { diff --git a/packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js b/packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js index 880b77a27abe7..ef0f494b687da 100644 --- a/packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js +++ b/packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js @@ -34,6 +34,7 @@ describe('StrictEffectsMode defaults', () => { assertLog = InternalTestUtils.assertLog; }); + // @gate !disableLegacyMode it('should not double invoke effects in legacy mode', async () => { function App({text}) { React.useEffect(() => { @@ -60,6 +61,7 @@ describe('StrictEffectsMode defaults', () => { assertLog(['useLayoutEffect mount', 'useEffect mount']); }); + // @gate !disableLegacyMode it('should not double invoke class lifecycles in legacy mode', async () => { class App extends React.PureComponent { componentDidMount() {