diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js index e57f084975d63..fec4cd6040b7e 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js @@ -882,7 +882,11 @@ describe('ReactHooksInspectionIntegration', () => { await LazyFoo; - Scheduler.unstable_flushAll(); + expect(() => { + Scheduler.unstable_flushAll(); + }).toErrorDev([ + 'Foo: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.', + ]); const childFiber = renderer.root._currentFiber(); const tree = ReactDebugTools.inspectHooksOfFiber(childFiber); diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 703e3280da58b..6f9cea6946b37 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -264,6 +264,7 @@ describe('ReactDOMFizzServer', () => { } // @gate experimental + // @gate !warnAboutDefaultPropsOnFunctionComponents || !__DEV__ it('should asynchronously load a lazy component', async () => { let resolveA; const LazyA = React.lazy(() => { diff --git a/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.internal.js b/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js similarity index 82% rename from packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.internal.js rename to packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js index f65c6e85e834b..bb61c1ca0d26d 100644 --- a/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js @@ -10,7 +10,6 @@ 'use strict'; let React; -let ReactFeatureFlags; let ReactNoop; let Scheduler; let JSXDEVRuntime; @@ -19,19 +18,11 @@ describe('ReactDeprecationWarnings', () => { beforeEach(() => { jest.resetModules(); React = require('react'); - ReactFeatureFlags = require('shared/ReactFeatureFlags'); ReactNoop = require('react-noop-renderer'); Scheduler = require('scheduler'); if (__DEV__) { JSXDEVRuntime = require('react/jsx-dev-runtime'); } - ReactFeatureFlags.warnAboutDefaultPropsOnFunctionComponents = true; - ReactFeatureFlags.warnAboutStringRefs = true; - }); - - afterEach(() => { - ReactFeatureFlags.warnAboutDefaultPropsOnFunctionComponents = false; - ReactFeatureFlags.warnAboutStringRefs = false; }); it('should warn when given defaultProps', () => { @@ -51,6 +42,27 @@ describe('ReactDeprecationWarnings', () => { ); }); + it('should warn when given defaultProps on a memoized function', () => { + const MemoComponent = React.memo(function FunctionalComponent(props) { + return null; + }); + + MemoComponent.defaultProps = { + testProp: true, + }; + + ReactNoop.render( +
+ +
, + ); + expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( + 'Warning: FunctionalComponent: Support for defaultProps ' + + 'will be removed from memo components in a future major ' + + 'release. Use JavaScript default parameters instead.', + ); + }); + it('should warn when given string refs', () => { class RefComponent extends React.Component { render() { @@ -74,9 +86,7 @@ describe('ReactDeprecationWarnings', () => { ); }); - it('should not warn when owner and self are the same for string refs', () => { - ReactFeatureFlags.warnAboutStringRefs = false; - + it('should warn when owner and self are the same for string refs', () => { class RefComponent extends React.Component { render() { return null; @@ -87,7 +97,11 @@ describe('ReactDeprecationWarnings', () => { return ; } } - ReactNoop.renderLegacySyncRoot(); + expect(() => { + ReactNoop.renderLegacySyncRoot(); + }).toErrorDev([ + 'Component "Component" contains the string ref "refComponent". Support for string refs will be removed in a future major release.', + ]); expect(Scheduler).toFlushWithoutYielding(); }); diff --git a/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js b/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js index 9228316f884ee..a9d12de7e9dd9 100644 --- a/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js @@ -367,11 +367,12 @@ describe('ReactFunctionComponent', () => { Child.defaultProps = {test: 2}; Child.propTypes = {test: PropTypes.string}; - expect(() => ReactTestUtils.renderIntoDocument()).toErrorDev( + expect(() => ReactTestUtils.renderIntoDocument()).toErrorDev([ + 'Warning: Child: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.', 'Warning: Failed prop type: Invalid prop `test` of type `number` ' + 'supplied to `Child`, expected `string`.\n' + ' in Child (at **)', - ); + ]); }); it('should receive context', () => { diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index bda70790734dc..77aeb467d748d 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -496,6 +496,20 @@ function updateMemoComponent( getComponentNameFromType(type), ); } + if ( + warnAboutDefaultPropsOnFunctionComponents && + Component.defaultProps !== undefined + ) { + const componentName = getComponentNameFromType(type) || 'Unknown'; + if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) { + console.error( + '%s: Support for defaultProps will be removed from memo components ' + + 'in a future major release. Use JavaScript default parameters instead.', + componentName, + ); + didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true; + } + } } const child = createFiberFromTypeAndProps( Component.type, diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index 5050ae80d85ce..a6e34dc926548 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -496,6 +496,20 @@ function updateMemoComponent( getComponentNameFromType(type), ); } + if ( + warnAboutDefaultPropsOnFunctionComponents && + Component.defaultProps !== undefined + ) { + const componentName = getComponentNameFromType(type) || 'Unknown'; + if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) { + console.error( + '%s: Support for defaultProps will be removed from memo components ' + + 'in a future major release. Use JavaScript default parameters instead.', + componentName, + ); + didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true; + } + } } const child = createFiberFromTypeAndProps( Component.type, diff --git a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js index 01e9742259365..f6d52b06bda61 100644 --- a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js @@ -293,7 +293,12 @@ describe('ReactLazy', () => { await Promise.resolve(); - expect(Scheduler).toFlushAndYield(['Hi']); + expect(() => expect(Scheduler).toFlushAndYield(['Hi'])).toErrorDev( + 'Warning: T: Support for defaultProps ' + + 'will be removed from function components in a future major ' + + 'release. Use JavaScript default parameters instead.', + ); + expect(root).toMatchRenderedOutput('Hi'); T.defaultProps = {text: 'Hi again'}; @@ -343,7 +348,14 @@ describe('ReactLazy', () => { await Promise.resolve(); - expect(Scheduler).toFlushAndYield(['Lazy', 'Sibling', 'A']); + expect(() => + expect(Scheduler).toFlushAndYield(['Lazy', 'Sibling', 'A']), + ).toErrorDev( + 'Warning: LazyImpl: Support for defaultProps ' + + 'will be removed from function components in a future major ' + + 'release. Use JavaScript default parameters instead.', + ); + expect(root).toMatchRenderedOutput('SiblingA'); // Lazy should not re-render @@ -643,7 +655,12 @@ describe('ReactLazy', () => { expect(root).not.toMatchRenderedOutput('Hi Bye'); await Promise.resolve(); - expect(Scheduler).toFlushAndYield(['Hi Bye']); + expect(() => expect(Scheduler).toFlushAndYield(['Hi Bye'])).toErrorDev( + 'Warning: T: Support for defaultProps ' + + 'will be removed from function components in a future major ' + + 'release. Use JavaScript default parameters instead.', + ); + expect(root).toMatchRenderedOutput('Hi Bye'); root.update( @@ -732,7 +749,11 @@ describe('ReactLazy', () => { ); }); - async function verifyInnerPropTypesAreChecked(Add) { + async function verifyInnerPropTypesAreChecked( + Add, + shouldWarnAboutFunctionDefaultProps, + shouldWarnAboutMemoDefaultProps, + ) { const LazyAdd = lazy(() => fakeImport(Add)); expect(() => { LazyAdd.propTypes = {}; @@ -753,15 +774,28 @@ describe('ReactLazy', () => { ); expect(Scheduler).toFlushAndYield(['Loading...']); + expect(root).not.toMatchRenderedOutput('22'); // Mount await Promise.resolve(); expect(() => { Scheduler.unstable_flushAll(); - }).toErrorDev([ - 'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.', - ]); + }).toErrorDev( + shouldWarnAboutFunctionDefaultProps + ? [ + 'Add: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.', + 'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.', + ] + : shouldWarnAboutMemoDefaultProps + ? [ + 'Add: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.', + 'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.', + ] + : [ + 'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.', + ], + ); expect(root).toMatchRenderedOutput('22'); // Update @@ -792,7 +826,7 @@ describe('ReactLazy', () => { Add.defaultProps = { innerWithDefault: 42, }; - await verifyInnerPropTypesAreChecked(Add); + await verifyInnerPropTypesAreChecked(Add, true); }); it('respects propTypes on function component without defaultProps', async () => { @@ -874,7 +908,7 @@ describe('ReactLazy', () => { Add.defaultProps = { innerWithDefault: 42, }; - await verifyInnerPropTypesAreChecked(Add); + await verifyInnerPropTypesAreChecked(Add, false, true); }); it('respects propTypes on outer memo component without defaultProps', async () => { @@ -901,7 +935,7 @@ describe('ReactLazy', () => { Add.defaultProps = { innerWithDefault: 42, }; - await verifyInnerPropTypesAreChecked(React.memo(Add)); + await verifyInnerPropTypesAreChecked(React.memo(Add), true); }); it('respects propTypes on inner memo component without defaultProps', async () => { @@ -944,9 +978,10 @@ describe('ReactLazy', () => { await Promise.resolve(); expect(() => { expect(Scheduler).toFlushAndYield(['Inner default text']); - }).toErrorDev( + }).toErrorDev([ + 'T: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.', 'The prop `text` is marked as required in `T`, but its value is `undefined`', - ); + ]); expect(root).toMatchRenderedOutput('Inner default text'); // Update @@ -1058,7 +1093,11 @@ describe('ReactLazy', () => { // Mount await Promise.resolve(); - expect(Scheduler).toFlushWithoutYielding(); + expect(() => { + expect(Scheduler).toFlushWithoutYielding(); + }).toErrorDev( + 'Unknown: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.', + ); expect(root).toMatchRenderedOutput('4'); // Update (shallowly equal) @@ -1142,7 +1181,12 @@ describe('ReactLazy', () => { // Mount await Promise.resolve(); - expect(Scheduler).toFlushWithoutYielding(); + expect(() => { + expect(Scheduler).toFlushWithoutYielding(); + }).toErrorDev([ + 'Memo: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.', + 'Unknown: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.', + ]); expect(root).toMatchRenderedOutput('4'); // Update diff --git a/packages/react-reconciler/src/__tests__/ReactMemo-test.js b/packages/react-reconciler/src/__tests__/ReactMemo-test.js index fca80e8e691a7..73746605e539b 100644 --- a/packages/react-reconciler/src/__tests__/ReactMemo-test.js +++ b/packages/react-reconciler/src/__tests__/ReactMemo-test.js @@ -75,6 +75,7 @@ describe('memo', () => { } ReactNoop.render(); expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev([ + 'App: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.', 'Warning: Function components cannot be given refs. Attempts to access ' + 'this ref will fail.', ]); @@ -441,7 +442,11 @@ describe('memo', () => { ); expect(Scheduler).toFlushAndYield(['Loading...']); await Promise.resolve(); - expect(Scheduler).toFlushAndYield([15]); + expect(() => { + expect(Scheduler).toFlushAndYield([15]); + }).toErrorDev([ + 'Counter: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.', + ]); expect(ReactNoop.getChildren()).toEqual([span(15)]); // Should bail out because props have not changed @@ -552,7 +557,11 @@ describe('memo', () => { , ); - expect(Scheduler).toFlushWithoutYielding(); + expect(() => { + expect(Scheduler).toFlushWithoutYielding(); + }).toErrorDev([ + 'Inner: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.', + ]); // Mount expect(() => { diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 46d90c2d83096..6383c10590726 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -205,7 +205,7 @@ export const disableTextareaChildren = false; // Part of the simplification of React.createElement so we can eventually move // from React.createElement to React.jsx // https://github.com/reactjs/rfcs/blob/createlement-rfc/text/0000-create-element-changes.md -export const warnAboutDefaultPropsOnFunctionComponents = false; // deprecate later, not 18.0 +export const warnAboutDefaultPropsOnFunctionComponents = true; // deprecate later, not 18.0 // Enables a warning when trying to spread a 'key' to an element; // a deprecated pattern we want to get rid of in the future diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 05f464094490b..f5677003e9c09 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -39,7 +39,7 @@ export const warnAboutDeprecatedLifecycles = true; export const enableScopeAPI = false; export const enableCreateEventHandleAPI = false; export const enableSuspenseCallback = false; -export const warnAboutDefaultPropsOnFunctionComponents = false; +export const warnAboutDefaultPropsOnFunctionComponents = true; export const warnAboutStringRefs = true; export const disableLegacyContext = false; export const disableSchedulerTimeoutBasedOnReactExpirationTime = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 0cab29173fa27..2e73ea246826f 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -29,7 +29,7 @@ export const enableSchedulerDebugging = false; export const enableScopeAPI = false; export const enableCreateEventHandleAPI = false; export const enableSuspenseCallback = false; -export const warnAboutDefaultPropsOnFunctionComponents = false; +export const warnAboutDefaultPropsOnFunctionComponents = true; export const warnAboutStringRefs = true; export const disableLegacyContext = false; export const disableSchedulerTimeoutBasedOnReactExpirationTime = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index d4e9d62c7898e..fa6d9add99ead 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -29,7 +29,7 @@ export const enableSchedulerDebugging = false; export const enableScopeAPI = false; export const enableCreateEventHandleAPI = false; export const enableSuspenseCallback = false; -export const warnAboutDefaultPropsOnFunctionComponents = false; +export const warnAboutDefaultPropsOnFunctionComponents = true; export const warnAboutStringRefs = true; export const disableLegacyContext = false; export const disableSchedulerTimeoutBasedOnReactExpirationTime = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js index 9db1f0ee966cd..6bc07f8bb6f99 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js @@ -29,7 +29,7 @@ export const enableSchedulerDebugging = false; export const enableScopeAPI = false; export const enableCreateEventHandleAPI = false; export const enableSuspenseCallback = false; -export const warnAboutDefaultPropsOnFunctionComponents = false; +export const warnAboutDefaultPropsOnFunctionComponents = true; export const warnAboutStringRefs = true; export const disableLegacyContext = false; export const disableSchedulerTimeoutBasedOnReactExpirationTime = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index c30606233cb30..46021b2bdd53b 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -29,7 +29,7 @@ export const disableInputAttributeSyncing = false; export const enableScopeAPI = true; export const enableCreateEventHandleAPI = false; export const enableSuspenseCallback = true; -export const warnAboutDefaultPropsOnFunctionComponents = false; +export const warnAboutDefaultPropsOnFunctionComponents = true; export const warnAboutStringRefs = true; export const disableLegacyContext = false; export const disableSchedulerTimeoutBasedOnReactExpirationTime = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index 4d5d3026d564a..8022b1b2f6f14 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -29,7 +29,7 @@ export const enableSchedulerDebugging = false; export const enableScopeAPI = false; export const enableCreateEventHandleAPI = false; export const enableSuspenseCallback = false; -export const warnAboutDefaultPropsOnFunctionComponents = false; +export const warnAboutDefaultPropsOnFunctionComponents = true; export const warnAboutStringRefs = true; export const disableLegacyContext = false; export const disableSchedulerTimeoutBasedOnReactExpirationTime = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index 45be33b7747af..cc5e17fe776a5 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -29,7 +29,7 @@ export const enableSchedulerDebugging = false; export const enableScopeAPI = true; export const enableCreateEventHandleAPI = true; export const enableSuspenseCallback = true; -export const warnAboutDefaultPropsOnFunctionComponents = false; +export const warnAboutDefaultPropsOnFunctionComponents = true; export const warnAboutStringRefs = true; export const disableLegacyContext = __EXPERIMENTAL__; export const disableSchedulerTimeoutBasedOnReactExpirationTime = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 48d984e07bc2a..e79e9789f8622 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -64,7 +64,7 @@ export const enableSchedulerDebugging = true; export const warnAboutDeprecatedLifecycles = true; export const disableLegacyContext = __EXPERIMENTAL__; export const warnAboutStringRefs = true; -export const warnAboutDefaultPropsOnFunctionComponents = false; +export const warnAboutDefaultPropsOnFunctionComponents = true; export const enableGetInspectorDataForInstanceInProduction = false; export const enableCache = true;