From 8742dceb2279087170cede7af8d420d23f41a19c Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 21 Mar 2019 15:44:38 -0700 Subject: [PATCH] Enabled warnAboutDeprecatedLifecycles flag by default --- .../ReactComponentLifeCycle-test.internal.js | 115 -------- .../__tests__/ReactComponentLifeCycle-test.js | 256 ++++++++++++++---- .../ReactDOMServerLifecycles-test.internal.js | 120 -------- .../ReactDOMServerLifecycles-test.js | 96 ++++++- .../ReactServerRenderingHydration-test.js | 16 +- .../ReactStrictMode-test.internal.js | 77 +++--- ...eateReactClassIntegration-test.internal.js | 119 +------- .../createReactClassIntegration-test.js | 150 ++++++++-- packages/shared/ReactFeatureFlags.js | 2 +- .../forks/ReactFeatureFlags.native-oss.js | 2 +- .../forks/ReactFeatureFlags.persistent.js | 2 +- .../ReactFeatureFlags.test-renderer.www.js | 2 +- 12 files changed, 501 insertions(+), 456 deletions(-) delete mode 100644 packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.internal.js delete mode 100644 packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.internal.js diff --git a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.internal.js b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.internal.js deleted file mode 100644 index ed65a16f0124..000000000000 --- a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.internal.js +++ /dev/null @@ -1,115 +0,0 @@ -/** - * 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'; - -let React; -let ReactDOM; -let ReactFeatureFlags; - -describe('ReactComponentLifeCycle', () => { - beforeEach(() => { - jest.resetModules(); - - ReactFeatureFlags = require('shared/ReactFeatureFlags'); - ReactFeatureFlags.warnAboutDeprecatedLifecycles = true; - - React = require('react'); - ReactDOM = require('react-dom'); - }); - - afterEach(() => { - jest.resetModules(); - }); - - // TODO (RFC #6) Merge this back into ReactComponentLifeCycles-test once - // the 'warnAboutDeprecatedLifecycles' feature flag has been removed. - it('warns about deprecated unsafe lifecycles', function() { - class MyComponent extends React.Component { - componentWillMount() {} - componentWillReceiveProps() {} - componentWillUpdate() {} - render() { - return null; - } - } - - const container = document.createElement('div'); - expect(() => - ReactDOM.render(, container), - ).toLowPriorityWarnDev( - [ - 'componentWillMount is deprecated and will be removed in the next major version. ' + - 'Use componentDidMount instead. As a temporary workaround, ' + - 'you can rename to UNSAFE_componentWillMount.' + - '\n\nPlease update the following components: MyComponent', - 'componentWillReceiveProps is deprecated and will be removed in the next major version. ' + - 'Use static getDerivedStateFromProps instead.' + - '\n\nPlease update the following components: MyComponent', - 'componentWillUpdate is deprecated and will be removed in the next major version. ' + - 'Use componentDidUpdate instead. As a temporary workaround, ' + - 'you can rename to UNSAFE_componentWillUpdate.' + - '\n\nPlease update the following components: MyComponent', - ], - {withoutStack: true}, - ); - - // Dedupe check (update and instantiate new - ReactDOM.render(, container); - ReactDOM.render(, container); - }); - - describe('react-lifecycles-compat', () => { - const {polyfill} = require('react-lifecycles-compat'); - - it('should not warn for components with polyfilled getDerivedStateFromProps', () => { - class PolyfilledComponent extends React.Component { - state = {}; - static getDerivedStateFromProps() { - return null; - } - render() { - return null; - } - } - - polyfill(PolyfilledComponent); - - const container = document.createElement('div'); - ReactDOM.render( - - - , - container, - ); - }); - - it('should not warn for components with polyfilled getSnapshotBeforeUpdate', () => { - class PolyfilledComponent extends React.Component { - getSnapshotBeforeUpdate() { - return null; - } - componentDidUpdate() {} - render() { - return null; - } - } - - polyfill(PolyfilledComponent); - - const container = document.createElement('div'); - ReactDOM.render( - - - , - container, - ); - }); - }); -}); diff --git a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js index 05c70bac3ca9..fd286f1e68c8 100644 --- a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js +++ b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js @@ -702,8 +702,17 @@ describe('ReactComponentLifeCycle', () => { } const container = document.createElement('div'); - expect(() => ReactDOM.render(, container)).toWarnDev( - 'Unsafe legacy lifecycles will not be called for components using new component APIs.', + expect(() => { + expect(() => ReactDOM.render(, container)).toWarnDev( + 'Unsafe legacy lifecycles will not be called for components using new component APIs.', + {withoutStack: true}, + ); + }).toLowPriorityWarnDev( + [ + 'componentWillMount is deprecated', + 'componentWillReceiveProps is deprecated', + 'componentWillUpdate is deprecated', + ], {withoutStack: true}, ); }); @@ -730,8 +739,19 @@ describe('ReactComponentLifeCycle', () => { } const container = document.createElement('div'); - expect(() => ReactDOM.render(, container)).toWarnDev( - 'Unsafe legacy lifecycles will not be called for components using new component APIs.', + expect(() => { + expect(() => + ReactDOM.render(, container), + ).toWarnDev( + 'Unsafe legacy lifecycles will not be called for components using new component APIs.', + {withoutStack: true}, + ); + }).toLowPriorityWarnDev( + [ + 'componentWillMount is deprecated', + 'componentWillReceiveProps is deprecated', + 'componentWillUpdate is deprecated', + ], {withoutStack: true}, ); ReactDOM.render(, container); @@ -781,14 +801,21 @@ describe('ReactComponentLifeCycle', () => { } } - expect(() => ReactDOM.render(, container)).toWarnDev( - 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + - 'AllLegacyLifecycles uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' + - ' componentWillMount\n' + - ' UNSAFE_componentWillReceiveProps\n' + - ' componentWillUpdate\n\n' + - 'The above lifecycles should be removed. Learn more about this warning here:\n' + - 'https://fb.me/react-async-component-lifecycle-hooks', + expect(() => { + expect(() => + ReactDOM.render(, container), + ).toWarnDev( + 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + + 'AllLegacyLifecycles uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' + + ' componentWillMount\n' + + ' UNSAFE_componentWillReceiveProps\n' + + ' componentWillUpdate\n\n' + + 'The above lifecycles should be removed. Learn more about this warning here:\n' + + 'https://fb.me/react-async-component-lifecycle-hooks', + {withoutStack: true}, + ); + }).toLowPriorityWarnDev( + ['componentWillMount is deprecated', 'componentWillUpdate is deprecated'], {withoutStack: true}, ); @@ -824,15 +851,21 @@ describe('ReactComponentLifeCycle', () => { } } - expect(() => ReactDOM.render(, container)).toWarnDev( - 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + - 'WillMountAndUpdate uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' + - ' componentWillMount\n' + - ' UNSAFE_componentWillUpdate\n\n' + - 'The above lifecycles should be removed. Learn more about this warning here:\n' + - 'https://fb.me/react-async-component-lifecycle-hooks', - {withoutStack: true}, - ); + expect(() => { + expect(() => + ReactDOM.render(, container), + ).toWarnDev( + 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + + 'WillMountAndUpdate uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' + + ' componentWillMount\n' + + ' UNSAFE_componentWillUpdate\n\n' + + 'The above lifecycles should be removed. Learn more about this warning here:\n' + + 'https://fb.me/react-async-component-lifecycle-hooks', + {withoutStack: true}, + ); + }).toLowPriorityWarnDev(['componentWillMount is deprecated'], { + withoutStack: true, + }); class WillReceiveProps extends React.Component { state = {}; @@ -845,14 +878,18 @@ describe('ReactComponentLifeCycle', () => { } } - expect(() => ReactDOM.render(, container)).toWarnDev( - 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + - 'WillReceiveProps uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' + - ' componentWillReceiveProps\n\n' + - 'The above lifecycles should be removed. Learn more about this warning here:\n' + - 'https://fb.me/react-async-component-lifecycle-hooks', - {withoutStack: true}, - ); + expect(() => { + expect(() => ReactDOM.render(, container)).toWarnDev( + 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + + 'WillReceiveProps uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' + + ' componentWillReceiveProps\n\n' + + 'The above lifecycles should be removed. Learn more about this warning here:\n' + + 'https://fb.me/react-async-component-lifecycle-hooks', + {withoutStack: true}, + ); + }).toLowPriorityWarnDev(['componentWillReceiveProps is deprecated'], { + withoutStack: true, + }); }); it('should warn about deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present', () => { @@ -870,14 +907,21 @@ describe('ReactComponentLifeCycle', () => { } } - expect(() => ReactDOM.render(, container)).toWarnDev( - 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + - 'AllLegacyLifecycles uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' + - ' componentWillMount\n' + - ' UNSAFE_componentWillReceiveProps\n' + - ' componentWillUpdate\n\n' + - 'The above lifecycles should be removed. Learn more about this warning here:\n' + - 'https://fb.me/react-async-component-lifecycle-hooks', + expect(() => { + expect(() => + ReactDOM.render(, container), + ).toWarnDev( + 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + + 'AllLegacyLifecycles uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' + + ' componentWillMount\n' + + ' UNSAFE_componentWillReceiveProps\n' + + ' componentWillUpdate\n\n' + + 'The above lifecycles should be removed. Learn more about this warning here:\n' + + 'https://fb.me/react-async-component-lifecycle-hooks', + {withoutStack: true}, + ); + }).toLowPriorityWarnDev( + ['componentWillMount is deprecated', 'componentWillUpdate is deprecated'], {withoutStack: true}, ); @@ -911,15 +955,21 @@ describe('ReactComponentLifeCycle', () => { } } - expect(() => ReactDOM.render(, container)).toWarnDev( - 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + - 'WillMountAndUpdate uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' + - ' componentWillMount\n' + - ' UNSAFE_componentWillUpdate\n\n' + - 'The above lifecycles should be removed. Learn more about this warning here:\n' + - 'https://fb.me/react-async-component-lifecycle-hooks', - {withoutStack: true}, - ); + expect(() => { + expect(() => + ReactDOM.render(, container), + ).toWarnDev( + 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + + 'WillMountAndUpdate uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' + + ' componentWillMount\n' + + ' UNSAFE_componentWillUpdate\n\n' + + 'The above lifecycles should be removed. Learn more about this warning here:\n' + + 'https://fb.me/react-async-component-lifecycle-hooks', + {withoutStack: true}, + ); + }).toLowPriorityWarnDev(['componentWillMount is deprecated'], { + withoutStack: true, + }); class WillReceiveProps extends React.Component { state = {}; @@ -931,14 +981,18 @@ describe('ReactComponentLifeCycle', () => { } } - expect(() => ReactDOM.render(, container)).toWarnDev( - 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + - 'WillReceiveProps uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' + - ' componentWillReceiveProps\n\n' + - 'The above lifecycles should be removed. Learn more about this warning here:\n' + - 'https://fb.me/react-async-component-lifecycle-hooks', - {withoutStack: true}, - ); + expect(() => { + expect(() => ReactDOM.render(, container)).toWarnDev( + 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + + 'WillReceiveProps uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' + + ' componentWillReceiveProps\n\n' + + 'The above lifecycles should be removed. Learn more about this warning here:\n' + + 'https://fb.me/react-async-component-lifecycle-hooks', + {withoutStack: true}, + ); + }).toLowPriorityWarnDev(['componentWillReceiveProps is deprecated'], { + withoutStack: true, + }); }); it('calls effects on module-pattern component', function() { @@ -1072,7 +1126,16 @@ describe('ReactComponentLifeCycle', () => { } const div = document.createElement('div'); - ReactDOM.render(, div); + expect(() => + ReactDOM.render(, div), + ).toLowPriorityWarnDev( + [ + 'componentWillMount is deprecated', + 'componentWillReceiveProps is deprecated', + 'componentWillUpdate is deprecated', + ], + {withoutStack: true}, + ); expect(log).toEqual(['componentWillMount', 'UNSAFE_componentWillMount']); log.length = 0; @@ -1324,4 +1387,87 @@ describe('ReactComponentLifeCycle', () => { // De-duped ReactDOM.render(, div); }); + + it('warns about deprecated unsafe lifecycles', function() { + class MyComponent extends React.Component { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + render() { + return null; + } + } + + const container = document.createElement('div'); + expect(() => + ReactDOM.render(, container), + ).toLowPriorityWarnDev( + [ + 'componentWillMount is deprecated and will be removed in the next major version. ' + + 'Use componentDidMount instead. As a temporary workaround, ' + + 'you can rename to UNSAFE_componentWillMount.' + + '\n\nPlease update the following components: MyComponent', + 'componentWillReceiveProps is deprecated and will be removed in the next major version. ' + + 'Use static getDerivedStateFromProps instead.' + + '\n\nPlease update the following components: MyComponent', + 'componentWillUpdate is deprecated and will be removed in the next major version. ' + + 'Use componentDidUpdate instead. As a temporary workaround, ' + + 'you can rename to UNSAFE_componentWillUpdate.' + + '\n\nPlease update the following components: MyComponent', + ], + {withoutStack: true}, + ); + + // Dedupe check (update and instantiate new + ReactDOM.render(, container); + ReactDOM.render(, container); + }); + + describe('react-lifecycles-compat', () => { + const {polyfill} = require('react-lifecycles-compat'); + + it('should not warn for components with polyfilled getDerivedStateFromProps', () => { + class PolyfilledComponent extends React.Component { + state = {}; + static getDerivedStateFromProps() { + return null; + } + render() { + return null; + } + } + + polyfill(PolyfilledComponent); + + const container = document.createElement('div'); + ReactDOM.render( + + + , + container, + ); + }); + + it('should not warn for components with polyfilled getSnapshotBeforeUpdate', () => { + class PolyfilledComponent extends React.Component { + getSnapshotBeforeUpdate() { + return null; + } + componentDidUpdate() {} + render() { + return null; + } + } + + polyfill(PolyfilledComponent); + + const container = document.createElement('div'); + ReactDOM.render( + + + , + container, + ); + }); + }); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.internal.js deleted file mode 100644 index 0e3f2a70ff69..000000000000 --- a/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.internal.js +++ /dev/null @@ -1,120 +0,0 @@ -/** - * 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'; - -let React; -let ReactFeatureFlags; -let ReactDOMServer; - -describe('ReactDOMServerLifecycles', () => { - beforeEach(() => { - ReactFeatureFlags = require('shared/ReactFeatureFlags'); - ReactFeatureFlags.warnAboutDeprecatedLifecycles = true; - - React = require('react'); - ReactDOMServer = require('react-dom/server'); - }); - - afterEach(() => { - jest.resetModules(); - }); - - it('should not invoke cWM if static gDSFP is present', () => { - class Component extends React.Component { - state = {}; - static getDerivedStateFromProps() { - return null; - } - componentWillMount() { - throw Error('unexpected'); - } - render() { - return null; - } - } - - expect(() => - ReactDOMServer.renderToString(), - ).toLowPriorityWarnDev( - 'Component: componentWillMount() is deprecated and will be removed in the next major version.', - {withoutStack: true}, - ); - }); - - // TODO (RFC #6) Merge this back into ReactDOMServerLifecycles-test once - // the 'warnAboutDeprecatedLifecycles' feature flag has been removed. - it('should warn about deprecated lifecycle hooks', () => { - class Component extends React.Component { - componentWillMount() {} - render() { - return null; - } - } - - expect(() => - ReactDOMServer.renderToString(), - ).toLowPriorityWarnDev( - 'Warning: Component: componentWillMount() is deprecated and will be removed ' + - 'in the next major version.', - {withoutStack: true}, - ); - - // De-duped - ReactDOMServer.renderToString(); - }); - - describe('react-lifecycles-compat', () => { - const {polyfill} = require('react-lifecycles-compat'); - - it('should not warn for components with polyfilled getDerivedStateFromProps', () => { - class PolyfilledComponent extends React.Component { - state = {}; - static getDerivedStateFromProps() { - return null; - } - render() { - return null; - } - } - - polyfill(PolyfilledComponent); - - const container = document.createElement('div'); - ReactDOMServer.renderToString( - - - , - container, - ); - }); - - it('should not warn for components with polyfilled getSnapshotBeforeUpdate', () => { - class PolyfilledComponent extends React.Component { - getSnapshotBeforeUpdate() { - return null; - } - componentDidUpdate() {} - render() { - return null; - } - } - - polyfill(PolyfilledComponent); - - const container = document.createElement('div'); - ReactDOMServer.renderToString( - - - , - container, - ); - }); - }); -}); diff --git a/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js b/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js index ad748e84e3b2..76230a323035 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js @@ -227,7 +227,11 @@ describe('ReactDOMServerLifecycles', () => { } } - ReactDOMServer.renderToString(); + expect(() => + ReactDOMServer.renderToString(), + ).toLowPriorityWarnDev('componentWillMount() is deprecated', { + withoutStack: true, + }); expect(log).toEqual(['componentWillMount', 'UNSAFE_componentWillMount']); }); @@ -265,4 +269,94 @@ describe('ReactDOMServerLifecycles', () => { {withoutStack: true}, ); }); + + it('should not invoke cWM if static gDSFP is present', () => { + class Component extends React.Component { + state = {}; + static getDerivedStateFromProps() { + return null; + } + componentWillMount() { + throw Error('unexpected'); + } + render() { + return null; + } + } + + expect(() => + ReactDOMServer.renderToString(), + ).toLowPriorityWarnDev( + 'Component: componentWillMount() is deprecated and will be removed in the next major version.', + {withoutStack: true}, + ); + }); + + it('should warn about deprecated lifecycle hooks', () => { + class Component extends React.Component { + componentWillMount() {} + render() { + return null; + } + } + + expect(() => + ReactDOMServer.renderToString(), + ).toLowPriorityWarnDev( + 'Warning: Component: componentWillMount() is deprecated and will be removed ' + + 'in the next major version.', + {withoutStack: true}, + ); + + // De-duped + ReactDOMServer.renderToString(); + }); + + describe('react-lifecycles-compat', () => { + const {polyfill} = require('react-lifecycles-compat'); + + it('should not warn for components with polyfilled getDerivedStateFromProps', () => { + class PolyfilledComponent extends React.Component { + state = {}; + static getDerivedStateFromProps() { + return null; + } + render() { + return null; + } + } + + polyfill(PolyfilledComponent); + + const container = document.createElement('div'); + ReactDOMServer.renderToString( + + + , + container, + ); + }); + + it('should not warn for components with polyfilled getSnapshotBeforeUpdate', () => { + class PolyfilledComponent extends React.Component { + getSnapshotBeforeUpdate() { + return null; + } + componentDidUpdate() {} + render() { + return null; + } + } + + polyfill(PolyfilledComponent); + + const container = document.createElement('div'); + ReactDOMServer.renderToString( + + + , + container, + ); + }); + }); }); diff --git a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js index c135cb221a86..e28183e1b323 100644 --- a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js +++ b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js @@ -358,11 +358,21 @@ describe('ReactDOMServerHydration', () => { ); const element = document.createElement('div'); - element.innerHTML = ReactDOMServer.renderToString(markup); + expect(() => { + element.innerHTML = ReactDOMServer.renderToString(markup); + }).toLowPriorityWarnDev( + ['componentWillMount() is deprecated and will be removed'], + {withoutStack: true}, + ); expect(element.textContent).toBe('Hi'); - expect(() => ReactDOM.hydrate(markup, element)).toWarnDev( - 'Please update the following components to use componentDidMount instead: ComponentWithWarning', + expect(() => { + expect(() => ReactDOM.hydrate(markup, element)).toWarnDev( + 'Please update the following components to use componentDidMount instead: ComponentWithWarning', + ); + }).toLowPriorityWarnDev( + ['componentWillMount is deprecated and will be removed'], + {withoutStack: true}, ); expect(element.textContent).toBe('Hi'); }); diff --git a/packages/react/src/__tests__/ReactStrictMode-test.internal.js b/packages/react/src/__tests__/ReactStrictMode-test.internal.js index 7c0b308e67cc..14b33ce99180 100644 --- a/packages/react/src/__tests__/ReactStrictMode-test.internal.js +++ b/packages/react/src/__tests__/ReactStrictMode-test.internal.js @@ -411,20 +411,29 @@ describe('ReactStrictMode', () => { let rendered; - expect( - () => (rendered = ReactTestRenderer.create()), - ).toWarnDev( - 'Unsafe lifecycle methods were found within a strict-mode tree:' + - '\n in ConcurrentMode (at **)' + - '\n in SyncRoot (at **)' + - '\n\ncomponentWillMount: Please update the following components ' + - 'to use componentDidMount instead: AsyncRoot, Parent' + - '\n\ncomponentWillReceiveProps: Please update the following components ' + - 'to use static getDerivedStateFromProps instead: Child, Parent' + - '\n\ncomponentWillUpdate: Please update the following components ' + - 'to use componentDidUpdate instead: AsyncRoot, Parent' + - '\n\nLearn more about this warning here:' + - '\nhttps://fb.me/react-strict-mode-warnings', + expect(() => { + expect( + () => (rendered = ReactTestRenderer.create()), + ).toWarnDev( + 'Unsafe lifecycle methods were found within a strict-mode tree:' + + '\n in ConcurrentMode (at **)' + + '\n in SyncRoot (at **)' + + '\n\ncomponentWillMount: Please update the following components ' + + 'to use componentDidMount instead: AsyncRoot, Parent' + + '\n\ncomponentWillReceiveProps: Please update the following components ' + + 'to use static getDerivedStateFromProps instead: Child, Parent' + + '\n\ncomponentWillUpdate: Please update the following components ' + + 'to use componentDidUpdate instead: AsyncRoot, Parent' + + '\n\nLearn more about this warning here:' + + '\nhttps://fb.me/react-strict-mode-warnings', + ); + }).toLowPriorityWarnDev( + [ + 'componentWillMount is deprecated', + 'componentWillReceiveProps is deprecated', + 'componentWillUpdate is deprecated', + ], + {withoutStack: true}, ); // Dedupe @@ -489,24 +498,28 @@ describe('ReactStrictMode', () => { let rendered; - expect( - () => (rendered = ReactTestRenderer.create()), - ).toWarnDev([ - 'Unsafe lifecycle methods were found within a strict-mode tree:' + - '\n in ConcurrentMode (at **)' + - '\n in AsyncRootOne (at **)' + - '\n in div (at **)' + - '\n in SyncRoot (at **)' + - '\n\ncomponentWillMount: Please update the following components ' + - 'to use componentDidMount instead: Bar, Foo', - 'Unsafe lifecycle methods were found within a strict-mode tree:' + - '\n in ConcurrentMode (at **)' + - '\n in AsyncRootTwo (at **)' + - '\n in div (at **)' + - '\n in SyncRoot (at **)' + - '\n\ncomponentWillMount: Please update the following components ' + - 'to use componentDidMount instead: Baz', - ]); + expect(() => { + expect( + () => (rendered = ReactTestRenderer.create()), + ).toWarnDev([ + 'Unsafe lifecycle methods were found within a strict-mode tree:' + + '\n in ConcurrentMode (at **)' + + '\n in AsyncRootOne (at **)' + + '\n in div (at **)' + + '\n in SyncRoot (at **)' + + '\n\ncomponentWillMount: Please update the following components ' + + 'to use componentDidMount instead: Bar, Foo', + 'Unsafe lifecycle methods were found within a strict-mode tree:' + + '\n in ConcurrentMode (at **)' + + '\n in AsyncRootTwo (at **)' + + '\n in div (at **)' + + '\n in SyncRoot (at **)' + + '\n\ncomponentWillMount: Please update the following components ' + + 'to use componentDidMount instead: Baz', + ]); + }).toLowPriorityWarnDev(['componentWillMount is deprecated'], { + withoutStack: true, + }); // Dedupe rendered = ReactTestRenderer.create(); diff --git a/packages/react/src/__tests__/createReactClassIntegration-test.internal.js b/packages/react/src/__tests__/createReactClassIntegration-test.internal.js index 16c10c370add..b1bd7c9b1c91 100644 --- a/packages/react/src/__tests__/createReactClassIntegration-test.internal.js +++ b/packages/react/src/__tests__/createReactClassIntegration-test.internal.js @@ -9,119 +9,24 @@ 'use strict'; -let React; -let ReactFeatureFlags; -let createReactClass; - describe('create-react-class-integration', () => { - beforeEach(() => { - jest.resetModules(); - - ReactFeatureFlags = require('shared/ReactFeatureFlags'); - ReactFeatureFlags.warnAboutDeprecatedLifecycles = true; - - React = require('react'); - createReactClass = require('create-react-class/factory')( - React.Component, - React.isValidElement, - new React.Component().updater, - ); - }); - - // TODO (RFC #6) Merge this back into createReactClassIntegration-test once - // the 'warnAboutDeprecatedLifecycles' feature flag has been removed. - it('isMounted works', () => { - const ReactDOM = require('react-dom'); - - const ops = []; - let instance; - const Component = createReactClass({ - displayName: 'MyComponent', - mixins: [ - { - UNSAFE_componentWillMount() { - this.log('mixin.componentWillMount'); - }, - componentDidMount() { - this.log('mixin.componentDidMount'); - }, - UNSAFE_componentWillUpdate() { - this.log('mixin.componentWillUpdate'); - }, - componentDidUpdate() { - this.log('mixin.componentDidUpdate'); - }, - componentWillUnmount() { - this.log('mixin.componentWillUnmount'); - }, - }, - ], - log(name) { - ops.push(`${name}: ${this.isMounted()}`); - }, - getInitialState() { - this.log('getInitialState'); - return {}; - }, - UNSAFE_componentWillMount() { - this.log('componentWillMount'); - }, - componentDidMount() { - this.log('componentDidMount'); - }, - UNSAFE_componentWillUpdate() { - this.log('componentWillUpdate'); - }, - componentDidUpdate() { - this.log('componentDidUpdate'); - }, - componentWillUnmount() { - this.log('componentWillUnmount'); - }, - render() { - instance = this; - this.log('render'); - return
; - }, - }); - - const container = document.createElement('div'); - - expect(() => ReactDOM.render(, container)).toWarnDev( - 'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' + - 'clean up subscriptions and pending requests in componentWillUnmount ' + - 'to prevent memory leaks.', - {withoutStack: true}, - ); - - // Dedupe - ReactDOM.render(, container); - - ReactDOM.unmountComponentAtNode(container); - instance.log('after unmount'); - expect(ops).toEqual([ - 'getInitialState: false', - 'mixin.componentWillMount: false', - 'componentWillMount: false', - 'render: false', - 'mixin.componentDidMount: true', - 'componentDidMount: true', - 'mixin.componentWillUpdate: true', - 'componentWillUpdate: true', - 'render: true', - 'mixin.componentDidUpdate: true', - 'componentDidUpdate: true', - 'mixin.componentWillUnmount: true', - 'componentWillUnmount: true', - 'after unmount: false', - ]); - }); - describe('ReactNative NativeMethodsMixin', () => { + let React; let ReactNative; let NativeMethodsMixin; + let createReactClass; beforeEach(() => { + jest.resetModules(); + + React = require('react'); + + createReactClass = require('create-react-class/factory')( + React.Component, + React.isValidElement, + new React.Component().updater, + ); + ReactNative = require('react-native-renderer'); NativeMethodsMixin = ReactNative.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED diff --git a/packages/react/src/__tests__/createReactClassIntegration-test.js b/packages/react/src/__tests__/createReactClassIntegration-test.js index efc2bf3ba49d..36b6bb047732 100644 --- a/packages/react/src/__tests__/createReactClassIntegration-test.js +++ b/packages/react/src/__tests__/createReactClassIntegration-test.js @@ -546,15 +546,24 @@ describe('create-react-class-integration', () => { }); expect(() => { - ReactDOM.render(, document.createElement('div')); - }).toWarnDev( - 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + - 'Component uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' + - ' componentWillMount\n' + - ' componentWillReceiveProps\n' + - ' componentWillUpdate\n\n' + - 'The above lifecycles should be removed. Learn more about this warning here:\n' + - 'https://fb.me/react-async-component-lifecycle-hooks', + expect(() => { + ReactDOM.render(, document.createElement('div')); + }).toWarnDev( + 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + + 'Component uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' + + ' componentWillMount\n' + + ' componentWillReceiveProps\n' + + ' componentWillUpdate\n\n' + + 'The above lifecycles should be removed. Learn more about this warning here:\n' + + 'https://fb.me/react-async-component-lifecycle-hooks', + {withoutStack: true}, + ); + }).toLowPriorityWarnDev( + [ + 'componentWillMount is deprecated', + 'componentWillReceiveProps is deprecated', + 'componentWillUpdate is deprecated', + ], {withoutStack: true}, ); ReactDOM.render(, document.createElement('div')); @@ -581,15 +590,24 @@ describe('create-react-class-integration', () => { }); expect(() => { - ReactDOM.render(, document.createElement('div')); - }).toWarnDev( - 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + - 'Component uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' + - ' componentWillMount\n' + - ' componentWillReceiveProps\n' + - ' componentWillUpdate\n\n' + - 'The above lifecycles should be removed. Learn more about this warning here:\n' + - 'https://fb.me/react-async-component-lifecycle-hooks', + expect(() => { + ReactDOM.render(, document.createElement('div')); + }).toWarnDev( + 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + + 'Component uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' + + ' componentWillMount\n' + + ' componentWillReceiveProps\n' + + ' componentWillUpdate\n\n' + + 'The above lifecycles should be removed. Learn more about this warning here:\n' + + 'https://fb.me/react-async-component-lifecycle-hooks', + {withoutStack: true}, + ); + }).toLowPriorityWarnDev( + [ + 'componentWillMount is deprecated', + 'componentWillReceiveProps is deprecated', + 'componentWillUpdate is deprecated', + ], {withoutStack: true}, ); ReactDOM.render(, document.createElement('div')); @@ -627,7 +645,16 @@ describe('create-react-class-integration', () => { }); const div = document.createElement('div'); - ReactDOM.render(, div); + expect(() => + ReactDOM.render(, div), + ).toLowPriorityWarnDev( + [ + 'componentWillMount is deprecated', + 'componentWillReceiveProps is deprecated', + 'componentWillUpdate is deprecated', + ], + {withoutStack: true}, + ); expect(log).toEqual(['componentWillMount', 'UNSAFE_componentWillMount']); log.length = 0; @@ -640,4 +667,89 @@ describe('create-react-class-integration', () => { 'UNSAFE_componentWillUpdate', ]); }); + + it('isMounted works', () => { + const ops = []; + let instance; + const Component = createReactClass({ + displayName: 'MyComponent', + mixins: [ + { + UNSAFE_componentWillMount() { + this.log('mixin.componentWillMount'); + }, + componentDidMount() { + this.log('mixin.componentDidMount'); + }, + UNSAFE_componentWillUpdate() { + this.log('mixin.componentWillUpdate'); + }, + componentDidUpdate() { + this.log('mixin.componentDidUpdate'); + }, + componentWillUnmount() { + this.log('mixin.componentWillUnmount'); + }, + }, + ], + log(name) { + ops.push(`${name}: ${this.isMounted()}`); + }, + getInitialState() { + this.log('getInitialState'); + return {}; + }, + UNSAFE_componentWillMount() { + this.log('componentWillMount'); + }, + componentDidMount() { + this.log('componentDidMount'); + }, + UNSAFE_componentWillUpdate() { + this.log('componentWillUpdate'); + }, + componentDidUpdate() { + this.log('componentDidUpdate'); + }, + componentWillUnmount() { + this.log('componentWillUnmount'); + }, + render() { + instance = this; + this.log('render'); + return
; + }, + }); + + const container = document.createElement('div'); + + expect(() => ReactDOM.render(, container)).toWarnDev( + 'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' + + 'clean up subscriptions and pending requests in componentWillUnmount ' + + 'to prevent memory leaks.', + {withoutStack: true}, + ); + + // Dedupe + ReactDOM.render(, container); + + ReactDOM.unmountComponentAtNode(container); + instance.log('after unmount'); + expect(ops).toEqual([ + 'getInitialState: false', + 'mixin.componentWillMount: false', + 'componentWillMount: false', + 'render: false', + 'mixin.componentDidMount: true', + 'componentDidMount: true', + 'mixin.componentWillUpdate: true', + 'componentWillUpdate: true', + 'render: true', + 'mixin.componentDidUpdate: true', + 'componentDidUpdate: true', + 'mixin.componentWillUnmount: true', + 'componentWillUnmount: true', + 'after unmount: false', + ]); + }); }); diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 9ace0090d6ca..990dedf24764 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -23,7 +23,7 @@ export const debugRenderPhaseSideEffectsForStrictMode = __DEV__; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; // Warn about deprecated, async-unsafe lifecycles; relates to RFC #6: -export const warnAboutDeprecatedLifecycles = false; +export const warnAboutDeprecatedLifecycles = true; // Gather advanced timing metrics for Profiler subtrees. export const enableProfilerTimer = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index e264fd3a3441..38e036682caa 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -16,7 +16,7 @@ export const debugRenderPhaseSideEffects = false; export const debugRenderPhaseSideEffectsForStrictMode = false; export const enableUserTimingAPI = __DEV__; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; -export const warnAboutDeprecatedLifecycles = false; +export const warnAboutDeprecatedLifecycles = true; export const enableProfilerTimer = __PROFILE__; export const enableSchedulerTracing = __PROFILE__; export const enableSuspenseServerRenderer = false; diff --git a/packages/shared/forks/ReactFeatureFlags.persistent.js b/packages/shared/forks/ReactFeatureFlags.persistent.js index 2b048a082236..e5dc81c58b32 100644 --- a/packages/shared/forks/ReactFeatureFlags.persistent.js +++ b/packages/shared/forks/ReactFeatureFlags.persistent.js @@ -15,7 +15,7 @@ import typeof * as PersistentFeatureFlagsType from './ReactFeatureFlags.persiste export const debugRenderPhaseSideEffects = false; export const debugRenderPhaseSideEffectsForStrictMode = false; export const enableUserTimingAPI = __DEV__; -export const warnAboutDeprecatedLifecycles = false; +export const warnAboutDeprecatedLifecycles = true; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__; export const enableProfilerTimer = __PROFILE__; export const enableSchedulerTracing = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 4b5df2e90408..97fc3164eb68 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -15,7 +15,7 @@ import typeof * as PersistentFeatureFlagsType from './ReactFeatureFlags.persiste export const debugRenderPhaseSideEffects = false; export const debugRenderPhaseSideEffectsForStrictMode = false; export const enableUserTimingAPI = __DEV__; -export const warnAboutDeprecatedLifecycles = false; +export const warnAboutDeprecatedLifecycles = true; export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = false; export const enableSchedulerTracing = false;