diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js index 2b93c8f06e2b2..e245adcb1380a 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js @@ -14,7 +14,6 @@ let React; let ReactTestRenderer; let ReactDebugTools; let act; -let assertConsoleErrorDev; let useMemoCache; function normalizeSourceLoc(tree) { @@ -34,7 +33,7 @@ describe('ReactHooksInspectionIntegration', () => { jest.resetModules(); React = require('react'); ReactTestRenderer = require('react-test-renderer'); - ({act, assertConsoleErrorDev} = require('internal-test-utils')); + ({act} = require('internal-test-utils')); ReactDebugTools = require('react-debug-tools'); useMemoCache = require('react/compiler-runtime').c; }); @@ -2321,57 +2320,6 @@ describe('ReactHooksInspectionIntegration', () => { }); }); - // @gate !disableDefaultPropsExceptForClasses - it('should support defaultProps and lazy', async () => { - const Suspense = React.Suspense; - - function Foo(props) { - const [value] = React.useState(props.defaultValue.slice(0, 3)); - return
{value}
; - } - Foo.defaultProps = { - defaultValue: 'default', - }; - - async function fakeImport(result) { - return {default: result}; - } - - const LazyFoo = React.lazy(() => fakeImport(Foo)); - - const renderer = ReactTestRenderer.create( - - - , - ); - - await act(async () => await LazyFoo); - assertConsoleErrorDev([ - '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); - expect(normalizeSourceLoc(tree)).toMatchInlineSnapshot(` - [ - { - "debugInfo": null, - "hookSource": { - "columnNumber": 0, - "fileName": "**", - "functionName": "Foo", - "lineNumber": 0, - }, - "id": 0, - "isStateEditable": true, - "name": "State", - "subHooks": [], - "value": "def", - }, - ] - `); - }); - // This test case is based on an open source bug report: // https://github.com/facebookincubator/redux-react-hook/issues/34#issuecomment-466693787 it('should properly advance the current hook for useContext', async () => { diff --git a/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js b/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js deleted file mode 100644 index 523f1167ab612..0000000000000 --- a/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and 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 ReactNoop; -let waitForAll; -let assertConsoleErrorDev; - -describe('ReactDeprecationWarnings', () => { - beforeEach(() => { - jest.resetModules(); - React = require('react'); - ReactNoop = require('react-noop-renderer'); - const InternalTestUtils = require('internal-test-utils'); - waitForAll = InternalTestUtils.waitForAll; - assertConsoleErrorDev = InternalTestUtils.assertConsoleErrorDev; - }); - - // @gate !disableDefaultPropsExceptForClasses || !__DEV__ - it('should warn when given defaultProps', async () => { - function FunctionalComponent(props) { - return null; - } - - FunctionalComponent.defaultProps = { - testProp: true, - }; - - ReactNoop.render(); - await waitForAll([]); - assertConsoleErrorDev([ - 'FunctionalComponent: Support for defaultProps ' + - 'will be removed from function components in a future major ' + - 'release. Use JavaScript default parameters instead.\n' + - ' in FunctionalComponent (at **)', - ]); - }); - - // @gate !disableDefaultPropsExceptForClasses || !__DEV__ - it('should warn when given defaultProps on a memoized function', async () => { - const MemoComponent = React.memo(function FunctionalComponent(props) { - return null; - }); - - MemoComponent.defaultProps = { - testProp: true, - }; - - ReactNoop.render( -
- -
, - ); - await waitForAll([]); - assertConsoleErrorDev( - [ - 'FunctionalComponent: Support for defaultProps ' + - 'will be removed from memo components in a future major ' + - 'release. Use JavaScript default parameters instead.', - ], - {withoutStack: true}, - ); - }); -}); diff --git a/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js b/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js index dc117f544cdf7..80f9779fdd17b 100644 --- a/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js @@ -205,27 +205,6 @@ describe('ReactFunctionComponent', () => { ]); }); - // @gate !disableDefaultPropsExceptForClasses - it('should support default props', async () => { - function Child(props) { - return
{props.test}
; - } - Child.defaultProps = {test: 2}; - - const container = document.createElement('div'); - const root = ReactDOMClient.createRoot(container); - - await act(() => { - root.render(); - }); - expect(container.textContent).toBe('2'); - assertConsoleErrorDev([ - 'Child: Support for defaultProps will be removed from function components in a future major release. ' + - 'Use JavaScript default parameters instead.\n' + - ' in Child (at **)', - ]); - }); - // @gate !disableLegacyContext && !disableLegacyContextForFunctionComponents it('should receive context', async () => { class Parent extends React.Component { diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index c195e840e6eb1..1b0b6b8d97310 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -117,7 +117,6 @@ import { enableCPUSuspense, enablePostpone, disableLegacyMode, - disableDefaultPropsExceptForClasses, enableHydrationLaneScheduling, enableViewTransition, enableFragmentRefs, @@ -258,7 +257,6 @@ import { updateClassInstance, resolveClassComponentProps, } from './ReactFiberClassComponent'; -import {resolveDefaultPropsOnNonClassComponent} from './ReactFiberLazyComponent'; import { createFiberFromTypeAndProps, createFiberFromFragment, @@ -327,7 +325,6 @@ let didWarnAboutGetDerivedStateOnFunctionComponent; export let didWarnAboutReassigningProps: boolean; let didWarnAboutRevealOrder; let didWarnAboutTailOptions; -let didWarnAboutDefaultPropsOnFunctionComponent; let didWarnAboutClassNameOnViewTransition; if (__DEV__) { @@ -338,7 +335,6 @@ if (__DEV__) { didWarnAboutReassigningProps = false; didWarnAboutRevealOrder = ({}: {[string]: boolean}); didWarnAboutTailOptions = ({}: {[string]: boolean}); - didWarnAboutDefaultPropsOnFunctionComponent = ({}: {[string]: boolean}); didWarnAboutClassNameOnViewTransition = ({}: {[string]: boolean}); } @@ -482,13 +478,7 @@ function updateMemoComponent( ): null | Fiber { if (current === null) { const type = Component.type; - if ( - isSimpleFunctionComponent(type) && - Component.compare === null && - // SimpleMemoComponent codepath doesn't resolve outer props either. - (disableDefaultPropsExceptForClasses || - Component.defaultProps === undefined) - ) { + if (isSimpleFunctionComponent(type) && Component.compare === null) { let resolvedType = type; if (__DEV__) { resolvedType = resolveFunctionForHotReloading(type); @@ -509,21 +499,6 @@ function updateMemoComponent( renderLanes, ); } - if (!disableDefaultPropsExceptForClasses) { - if (__DEV__) { - if (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, null, @@ -2072,9 +2047,6 @@ function mountLazyComponent( renderLanes, ); } else { - const resolvedProps = disableDefaultPropsExceptForClasses - ? props - : resolveDefaultPropsOnNonClassComponent(Component, props); workInProgress.tag = FunctionComponent; if (__DEV__) { validateFunctionComponentInDev(workInProgress, Component); @@ -2085,16 +2057,13 @@ function mountLazyComponent( null, workInProgress, Component, - resolvedProps, + props, renderLanes, ); } } else if (Component !== undefined && Component !== null) { const $$typeof = Component.$$typeof; if ($$typeof === REACT_FORWARD_REF_TYPE) { - const resolvedProps = disableDefaultPropsExceptForClasses - ? props - : resolveDefaultPropsOnNonClassComponent(Component, props); workInProgress.tag = ForwardRef; if (__DEV__) { workInProgress.type = Component = @@ -2104,24 +2073,16 @@ function mountLazyComponent( null, workInProgress, Component, - resolvedProps, + props, renderLanes, ); } else if ($$typeof === REACT_MEMO_TYPE) { - const resolvedProps = disableDefaultPropsExceptForClasses - ? props - : resolveDefaultPropsOnNonClassComponent(Component, props); workInProgress.tag = MemoComponent; return updateMemoComponent( null, workInProgress, Component, - disableDefaultPropsExceptForClasses - ? resolvedProps - : resolveDefaultPropsOnNonClassComponent( - Component.type, - resolvedProps, - ), // The inner type can have defaults too + props, renderLanes, ); } @@ -2198,22 +2159,6 @@ function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) { ); } - if ( - !disableDefaultPropsExceptForClasses && - Component.defaultProps !== undefined - ) { - const componentName = getComponentNameFromType(Component) || 'Unknown'; - - if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) { - console.error( - '%s: Support for defaultProps will be removed from function components ' + - 'in a future major release. Use JavaScript default parameters instead.', - componentName, - ); - didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true; - } - } - if (typeof Component.getDerivedStateFromProps === 'function') { const componentName = getComponentNameFromType(Component) || 'Unknown'; @@ -4175,17 +4120,11 @@ function beginWork( } case FunctionComponent: { const Component = workInProgress.type; - const unresolvedProps = workInProgress.pendingProps; - const resolvedProps = - disableDefaultPropsExceptForClasses || - workInProgress.elementType === Component - ? unresolvedProps - : resolveDefaultPropsOnNonClassComponent(Component, unresolvedProps); return updateFunctionComponent( current, workInProgress, Component, - resolvedProps, + workInProgress.pendingProps, renderLanes, ); } @@ -4226,18 +4165,11 @@ function beginWork( case HostPortal: return updatePortalComponent(current, workInProgress, renderLanes); case ForwardRef: { - const type = workInProgress.type; - const unresolvedProps = workInProgress.pendingProps; - const resolvedProps = - disableDefaultPropsExceptForClasses || - workInProgress.elementType === type - ? unresolvedProps - : resolveDefaultPropsOnNonClassComponent(type, unresolvedProps); return updateForwardRef( current, workInProgress, - type, - resolvedProps, + workInProgress.type, + workInProgress.pendingProps, renderLanes, ); } @@ -4252,20 +4184,11 @@ function beginWork( case ContextConsumer: return updateContextConsumer(current, workInProgress, renderLanes); case MemoComponent: { - const type = workInProgress.type; - const unresolvedProps = workInProgress.pendingProps; - // Resolve outer props first, then resolve inner props. - let resolvedProps = disableDefaultPropsExceptForClasses - ? unresolvedProps - : resolveDefaultPropsOnNonClassComponent(type, unresolvedProps); - resolvedProps = disableDefaultPropsExceptForClasses - ? resolvedProps - : resolveDefaultPropsOnNonClassComponent(type.type, resolvedProps); return updateMemoComponent( current, workInProgress, - type, - resolvedProps, + workInProgress.type, + workInProgress.pendingProps, renderLanes, ); } diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js index 7076c678833dc..7a9b443b3c282 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.js @@ -20,7 +20,6 @@ import { import { disableLegacyContext, enableSchedulingProfiler, - disableDefaultPropsExceptForClasses, } from 'shared/ReactFeatureFlags'; import ReactStrictModeWarnings from './ReactStrictModeWarnings'; import {get as getInstance, set as setInstance} from 'shared/ReactInstanceMap'; @@ -1214,12 +1213,7 @@ export function resolveClassComponentProps( // Resolve default props. const defaultProps = Component.defaultProps; - if ( - defaultProps && - // If disableDefaultPropsExceptForClasses is true, we always resolve - // default props here in the reconciler, rather than in the JSX runtime. - (disableDefaultPropsExceptForClasses || !alreadyResolvedDefaultProps) - ) { + if (defaultProps) { // We may have already copied the props object above to remove ref. If so, // we can modify that. Otherwise, copy the props object with Object.assign. if (newProps === baseProps) { diff --git a/packages/react-reconciler/src/ReactFiberLazyComponent.js b/packages/react-reconciler/src/ReactFiberLazyComponent.js deleted file mode 100644 index 36e16c2b40800..0000000000000 --- a/packages/react-reconciler/src/ReactFiberLazyComponent.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import assign from 'shared/assign'; -import {disableDefaultPropsExceptForClasses} from 'shared/ReactFeatureFlags'; - -export function resolveDefaultPropsOnNonClassComponent( - Component: any, - baseProps: Object, -): Object { - if (disableDefaultPropsExceptForClasses) { - // Support for defaultProps is removed in React 19 for all types - // except classes. - return baseProps; - } - if (Component && Component.defaultProps) { - // Resolve default props. Taken from ReactElement - const props = assign({}, baseProps); - const defaultProps = Component.defaultProps; - for (const propName in defaultProps) { - if (props[propName] === undefined) { - props[propName] = defaultProps[propName]; - } - } - return props; - } - return baseProps; -} diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index a8b5dfadeb736..1107c5bd4624d 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -46,7 +46,6 @@ import { alwaysThrottleRetries, enableInfiniteRenderLoopDetection, disableLegacyMode, - disableDefaultPropsExceptForClasses, enableComponentPerformanceTrack, enableYieldingBeforePassive, enableThrottledScheduling, @@ -349,7 +348,6 @@ import { getSuspenseHandler, getShellBoundary, } from './ReactFiberSuspenseContext'; -import {resolveDefaultPropsOnNonClassComponent} from './ReactFiberLazyComponent'; import {resetChildReconcilerOnUnwind} from './ReactChildFiber'; import { ensureRootIsScheduled, @@ -2858,12 +2856,6 @@ function replayBeginWork(unitOfWork: Fiber): null | Fiber { // could maybe use this as an opportunity to say `use` doesn't work with // `defaultProps` :) const Component = unitOfWork.type; - const unresolvedProps = unitOfWork.pendingProps; - const resolvedProps = - disableDefaultPropsExceptForClasses || - unitOfWork.elementType === Component - ? unresolvedProps - : resolveDefaultPropsOnNonClassComponent(Component, unresolvedProps); let context: any; if (!disableLegacyContext) { const unmaskedContext = getUnmaskedContext(unitOfWork, Component, true); @@ -2872,7 +2864,7 @@ function replayBeginWork(unitOfWork: Fiber): null | Fiber { next = replayFunctionComponent( current, unitOfWork, - resolvedProps, + unitOfWork.pendingProps, Component, context, workInProgressRootRenderLanes, @@ -2885,17 +2877,10 @@ function replayBeginWork(unitOfWork: Fiber): null | Fiber { // could maybe use this as an opportunity to say `use` doesn't work with // `defaultProps` :) const Component = unitOfWork.type.render; - const unresolvedProps = unitOfWork.pendingProps; - const resolvedProps = - disableDefaultPropsExceptForClasses || - unitOfWork.elementType === Component - ? unresolvedProps - : resolveDefaultPropsOnNonClassComponent(Component, unresolvedProps); - next = replayFunctionComponent( current, unitOfWork, - resolvedProps, + unitOfWork.pendingProps, Component, unitOfWork.ref, workInProgressRootRenderLanes, diff --git a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js index b6ae8e2ba1281..27404eee5e2c8 100644 --- a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js @@ -701,64 +701,6 @@ describe('ReactLazy', () => { expect(root).toMatchRenderedOutput('A3'); }); - // @gate !disableDefaultPropsExceptForClasses - it('resolves defaultProps on the outer wrapper but warns', async () => { - function T(props) { - Scheduler.log(props.inner + ' ' + props.outer); - return props.inner + ' ' + props.outer; - } - T.defaultProps = {inner: 'Hi'}; - const LazyText = lazy(() => fakeImport(T)); - LazyText.defaultProps = {outer: 'Bye'}; - assertConsoleErrorDev( - [ - 'It is not supported to assign `defaultProps` to ' + - 'a lazy component import. Either specify them where the component ' + - 'is defined, or create a wrapping component around it.', - ], - {withoutStack: true}, - ); - - const root = ReactTestRenderer.create( - }> - - , - { - unstable_isConcurrent: true, - }, - ); - - await waitForAll(['Loading...']); - expect(root).not.toMatchRenderedOutput('Hi Bye'); - - await act(() => resolveFakeImport(T)); - assertLog(['Hi Bye']); - assertConsoleErrorDev([ - 'T: Support for defaultProps ' + - 'will be removed from function components in a future major ' + - 'release. Use JavaScript default parameters instead.\n' + - ' in T (at **)', - ]); - - expect(root).toMatchRenderedOutput('Hi Bye'); - - root.update( - }> - - , - ); - await waitForAll(['Hi World']); - expect(root).toMatchRenderedOutput('Hi World'); - - root.update( - }> - - , - ); - await waitForAll(['Friends Bye']); - expect(root).toMatchRenderedOutput('Friends Bye'); - }); - it('throws with a useful error when wrapping invalid type with lazy()', async () => { const BadLazy = lazy(() => fakeImport(42)); @@ -1112,48 +1054,6 @@ describe('ReactLazy', () => { ); }); - // @gate !disableDefaultPropsExceptForClasses - it('resolves props for function component with defaultProps', async () => { - function Add(props) { - expect(props.innerWithDefault).toBe(42); - return props.inner + props.outer; - } - Add.defaultProps = { - innerWithDefault: 42, - }; - const LazyAdd = lazy(() => fakeImport(Add)); - const root = ReactTestRenderer.create( - }> - - , - { - unstable_isConcurrent: true, - }, - ); - - await waitForAll(['Loading...']); - expect(root).not.toMatchRenderedOutput('22'); - - // Mount - await act(() => resolveFakeImport(Add)); - - assertConsoleErrorDev([ - 'Add: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.\n' + - ' in Add (at **)', - ]); - - expect(root).toMatchRenderedOutput('22'); - - // Update - root.update( - }> - - , - ); - await waitForAll([]); - expect(root).toMatchRenderedOutput('0'); - }); - it('resolves props for function component without defaultProps', async () => { function Add(props) { return props.inner + props.outer; @@ -1258,44 +1158,6 @@ describe('ReactLazy', () => { expect(root).toMatchRenderedOutput('0'); }); - // @gate !disableDefaultPropsExceptForClasses - it('resolves props for forwardRef component with defaultProps', async () => { - const Add = React.forwardRef((props, ref) => { - expect(props.innerWithDefault).toBe(42); - return props.inner + props.outer; - }); - Add.displayName = 'Add'; - Add.defaultProps = { - innerWithDefault: 42, - }; - const LazyAdd = lazy(() => fakeImport(Add)); - const root = ReactTestRenderer.create( - }> - - , - { - unstable_isConcurrent: true, - }, - ); - - await waitForAll(['Loading...']); - expect(root).not.toMatchRenderedOutput('22'); - - // Mount - await act(() => resolveFakeImport(Add)); - - expect(root).toMatchRenderedOutput('22'); - - // Update - root.update( - }> - - , - ); - await waitForAll([]); - expect(root).toMatchRenderedOutput('0'); - }); - it('resolves props for forwardRef component without defaultProps', async () => { const Add = React.forwardRef((props, ref) => { return props.inner + props.outer; @@ -1330,51 +1192,6 @@ describe('ReactLazy', () => { expect(root).toMatchRenderedOutput('0'); }); - // @gate !disableDefaultPropsExceptForClasses - it('resolves props for outer memo component with defaultProps', async () => { - let Add = props => { - expect(props.innerWithDefault).toBe(42); - return props.inner + props.outer; - }; - Add = React.memo(Add); - Add.defaultProps = { - innerWithDefault: 42, - }; - const LazyAdd = lazy(() => fakeImport(Add)); - const root = ReactTestRenderer.create( - }> - - , - { - unstable_isConcurrent: true, - }, - ); - - await waitForAll(['Loading...']); - expect(root).not.toMatchRenderedOutput('22'); - - // Mount - await act(() => resolveFakeImport(Add)); - - assertConsoleErrorDev( - [ - 'Add: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.', - ], - {withoutStack: true}, - ); - - expect(root).toMatchRenderedOutput('22'); - - // Update - root.update( - }> - - , - ); - await waitForAll([]); - expect(root).toMatchRenderedOutput('0'); - }); - it('resolves props for outer memo component without defaultProps', async () => { let Add = props => { return props.inner + props.outer; @@ -1408,52 +1225,6 @@ describe('ReactLazy', () => { expect(root).toMatchRenderedOutput('0'); }); - // @gate !disableDefaultPropsExceptForClasses - it('resolves props for inner memo component with defaultProps', async () => { - const Add = props => { - expect(props.innerWithDefault).toBe(42); - return props.inner + props.outer; - }; - Add.displayName = 'Add'; - Add.defaultProps = { - innerWithDefault: 42, - }; - const MemoAdd = React.memo(Add); - const LazyAdd = lazy(() => fakeImport(MemoAdd)); - const root = ReactTestRenderer.create( - }> - - , - { - unstable_isConcurrent: true, - }, - ); - - await waitForAll(['Loading...']); - expect(root).not.toMatchRenderedOutput('22'); - - // Mount - await act(() => resolveFakeImport(MemoAdd)); - - assertConsoleErrorDev( - [ - 'Add: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.', - ], - {withoutStack: true}, - ); - - expect(root).toMatchRenderedOutput('22'); - - // Update - root.update( - }> - - , - ); - await waitForAll([]); - expect(root).toMatchRenderedOutput('0'); - }); - it('resolves props for inner memo component without defaultProps', async () => { const Add = props => { return props.inner + props.outer; @@ -1487,50 +1258,6 @@ describe('ReactLazy', () => { expect(root).toMatchRenderedOutput('0'); }); - // @gate !disableDefaultPropsExceptForClasses - it('uses outer resolved props on memo', async () => { - let T = props => { - return ; - }; - T.defaultProps = { - text: 'Inner default text', - }; - T = React.memo(T); - const LazyText = lazy(() => fakeImport(T)); - const root = ReactTestRenderer.create( - }> - - , - { - unstable_isConcurrent: true, - }, - ); - - await waitForAll(['Loading...']); - expect(root).not.toMatchRenderedOutput('Inner default text'); - - // Mount - await act(() => resolveFakeImport(T)); - assertLog(['Inner default text']); - assertConsoleErrorDev( - [ - 'T: Support for defaultProps will be removed from function components in a future major release. ' + - 'Use JavaScript default parameters instead.', - ], - {withoutStack: true}, - ); - expect(root).toMatchRenderedOutput('Inner default text'); - - // Update - root.update( - }> - - , - ); - await waitForAll([null]); - expect(root).toMatchRenderedOutput(null); - }); - it('includes lazy-loaded component in warning stack', async () => { const Foo = props =>
{[, ]}
; const LazyFoo = lazy(() => { @@ -1614,150 +1341,6 @@ describe('ReactLazy', () => { expect(ref.current).not.toBe(null); }); - // Regression test for #14310 - // @gate !disableDefaultPropsExceptForClasses - it('supports defaultProps defined on the memo() return value', async () => { - const Add = React.memo(props => { - return props.inner + props.outer; - }); - Add.defaultProps = { - inner: 2, - }; - const LazyAdd = lazy(() => fakeImport(Add)); - const root = ReactTestRenderer.create( - }> - - , - { - unstable_isConcurrent: true, - }, - ); - await waitForAll(['Loading...']); - expect(root).not.toMatchRenderedOutput('4'); - - // Mount - await act(() => resolveFakeImport(Add)); - assertConsoleErrorDev( - [ - 'Unknown: Support for defaultProps will be removed from memo components in a future major release. ' + - 'Use JavaScript default parameters instead.', - ], - {withoutStack: true}, - ); - expect(root).toMatchRenderedOutput('4'); - - // Update (shallowly equal) - root.update( - }> - - , - ); - await waitForAll([]); - expect(root).toMatchRenderedOutput('4'); - - // Update - root.update( - }> - - , - ); - await waitForAll([]); - expect(root).toMatchRenderedOutput('5'); - - // Update (shallowly equal) - root.update( - }> - - , - ); - await waitForAll([]); - expect(root).toMatchRenderedOutput('5'); - - // Update (explicit props) - root.update( - }> - - , - ); - await waitForAll([]); - expect(root).toMatchRenderedOutput('2'); - - // Update (explicit props, shallowly equal) - root.update( - }> - - , - ); - await waitForAll([]); - expect(root).toMatchRenderedOutput('2'); - - // Update - root.update( - }> - - , - ); - await waitForAll([]); - expect(root).toMatchRenderedOutput('3'); - }); - - // @gate !disableDefaultPropsExceptForClasses - it('merges defaultProps in the correct order', async () => { - let Add = React.memo(props => { - return props.inner + props.outer; - }); - Add.defaultProps = { - inner: 100, - }; - Add = React.memo(Add); - Add.defaultProps = { - inner: 2, - outer: 0, - }; - const LazyAdd = lazy(() => fakeImport(Add)); - const root = ReactTestRenderer.create( - }> - - , - { - unstable_isConcurrent: true, - }, - ); - await waitForAll(['Loading...']); - expect(root).not.toMatchRenderedOutput('4'); - - // Mount - await act(() => resolveFakeImport(Add)); - assertConsoleErrorDev( - [ - '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.', - ], - {withoutStack: true}, - ); - expect(root).toMatchRenderedOutput('4'); - - // Update - root.update( - }> - - , - ); - await waitForAll([]); - expect(root).toMatchRenderedOutput('5'); - - // Update - root.update( - }> - - , - ); - await waitForAll([]); - expect(root).toMatchRenderedOutput('2'); - }); - it('should error with a component stack naming the resolved component', async () => { let componentStackMessage; diff --git a/packages/react-reconciler/src/__tests__/ReactMemo-test.js b/packages/react-reconciler/src/__tests__/ReactMemo-test.js index 02f5e6ef18813..e96dc72e6a13b 100644 --- a/packages/react-reconciler/src/__tests__/ReactMemo-test.js +++ b/packages/react-reconciler/src/__tests__/ReactMemo-test.js @@ -373,77 +373,6 @@ describe('memo', () => { expect(ReactNoop).toMatchRenderedOutput(); }); - // @gate !disableDefaultPropsExceptForClasses - it('supports defaultProps defined on the memo() return value', async () => { - function Counter({a, b, c, d, e}) { - return ; - } - Counter.defaultProps = { - a: 1, - }; - // Note! We intentionally use React.memo() rather than the injected memo(). - // This tests a synchronous chain of React.memo() without lazy() in the middle. - Counter = React.memo(Counter); - Counter.defaultProps = { - b: 2, - }; - Counter = React.memo(Counter); - Counter = React.memo(Counter); // Layer without defaultProps - Counter.defaultProps = { - c: 3, - }; - Counter = React.memo(Counter); - Counter.defaultProps = { - d: 4, - }; - // The final layer uses memo() from test fixture (which might be lazy). - Counter = memo(Counter); - - await act(() => { - ReactNoop.render( - }> - - , - ); - }); - assertLog(['Loading...', 15]); - if (label === 'lazy') { - assertConsoleErrorDev( - [ - 'Counter: Support for defaultProps will be removed from memo components in a future major release. ' + - 'Use JavaScript default parameters instead.', - ], - {withoutStack: true}, - ); - } else { - assertConsoleErrorDev([ - 'Counter: Support for defaultProps will be removed from memo components in a future major release. ' + - 'Use JavaScript default parameters instead.\n' + - ' in Indirection (at **)', - ]); - } - - expect(ReactNoop).toMatchRenderedOutput(); - - // Should bail out because props have not changed - ReactNoop.render( - - - , - ); - await waitForAll([]); - expect(ReactNoop).toMatchRenderedOutput(); - - // Should update because count prop changed - ReactNoop.render( - - - , - ); - await waitForAll([20]); - expect(ReactNoop).toMatchRenderedOutput(); - }); - it('warns if the first argument is undefined', () => { memo(); assertConsoleErrorDev( @@ -466,46 +395,6 @@ describe('memo', () => { ); }); - // @gate !disableDefaultPropsExceptForClasses - it('handles nested defaultProps declarations', async () => { - function Inner(props) { - return props.inner + props.middle + props.outer; - } - Inner.defaultProps = {inner: 1}; - const Middle = React.memo(Inner); - Middle.defaultProps = {middle: 10}; - const Outer = React.memo(Middle); - Outer.defaultProps = {outer: 100}; - - const root = ReactNoop.createRoot(); - await act(() => { - root.render( -
- -
, - ); - }); - assertConsoleErrorDev( - [ - 'Inner: ' + - 'Support for defaultProps will be removed from memo components in a future major release. ' + - 'Use JavaScript default parameters instead.', - ], - {withoutStack: true}, - ); - expect(root).toMatchRenderedOutput(
111
); - - await act(async () => { - root.render( -
- -
, - ); - await waitForAll([]); - }); - expect(root).toMatchRenderedOutput(
234
); - }); - it('does not drop lower priority state updates when bailing out at higher pri (simple)', async () => { const {useState} = React; diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index 2f58c14e5df2f..732dc1067872f 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -178,7 +178,6 @@ import { enableScopeAPI, enablePostpone, enableHalt, - disableDefaultPropsExceptForClasses, enableAsyncIterableChildren, enableViewTransition, enableFizzBlockingRender, @@ -2399,12 +2398,7 @@ export function resolveClassComponentProps( // Resolve default props. const defaultProps = Component.defaultProps; - if ( - defaultProps && - // If disableDefaultPropsExceptForClasses is true, we always resolve - // default props here, rather than in the JSX runtime. - disableDefaultPropsExceptForClasses - ) { + if (defaultProps) { // We may have already copied the props object above to remove ref. If so, // we can modify that. Otherwise, copy the props object with Object.assign. if (newProps === baseProps) { @@ -2453,7 +2447,6 @@ const didWarnAboutContextTypes: {[string]: boolean} = {}; const didWarnAboutContextTypeOnFunctionComponent: {[string]: boolean} = {}; const didWarnAboutGetDerivedStateOnFunctionComponent: {[string]: boolean} = {}; let didWarnAboutReassigningProps = false; -const didWarnAboutDefaultPropsOnFunctionComponent: {[string]: boolean} = {}; let didWarnAboutGenerators = false; let didWarnAboutMaps = false; @@ -2610,22 +2603,6 @@ function validateFunctionComponentInDev(Component: any): void { ); } - if ( - !disableDefaultPropsExceptForClasses && - Component.defaultProps !== undefined - ) { - const componentName = getComponentNameFromType(Component) || 'Unknown'; - - if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) { - console.error( - '%s: Support for defaultProps will be removed from function components ' + - 'in a future major release. Use JavaScript default parameters instead.', - componentName, - ); - didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true; - } - } - if (typeof Component.getDerivedStateFromProps === 'function') { const componentName = getComponentNameFromType(Component) || 'Unknown'; @@ -2655,29 +2632,6 @@ function validateFunctionComponentInDev(Component: any): void { } } -function resolveDefaultPropsOnNonClassComponent( - Component: any, - baseProps: Object, -): Object { - if (disableDefaultPropsExceptForClasses) { - // Support for defaultProps is removed in React 19 for all types - // except classes. - return baseProps; - } - if (Component && Component.defaultProps) { - // Resolve default props. Taken from ReactElement - const props = assign({}, baseProps); - const defaultProps = Component.defaultProps; - for (const propName in defaultProps) { - if (props[propName] === undefined) { - props[propName] = defaultProps[propName]; - } - } - return props; - } - return baseProps; -} - function renderForwardRef( request: Request, task: Task, @@ -2735,11 +2689,7 @@ function renderMemo( ref: any, ): void { const innerType = type.type; - const resolvedProps = resolveDefaultPropsOnNonClassComponent( - innerType, - props, - ); - renderElement(request, task, keyPath, innerType, resolvedProps, ref); + renderElement(request, task, keyPath, innerType, props, ref); } function renderContextConsumer( @@ -2819,11 +2769,7 @@ function renderLazyComponent( // eslint-disable-next-line no-throw-literal throw null; } - const resolvedProps = resolveDefaultPropsOnNonClassComponent( - Component, - props, - ); - renderElement(request, task, keyPath, Component, resolvedProps, ref); + renderElement(request, task, keyPath, Component, props, ref); } function renderActivity( diff --git a/packages/react/src/ReactLazy.js b/packages/react/src/ReactLazy.js index d1d1088aaeb0d..2ac29c87774ec 100644 --- a/packages/react/src/ReactLazy.js +++ b/packages/react/src/ReactLazy.js @@ -10,7 +10,6 @@ import type {Wakeable, Thenable, ReactDebugInfo} from 'shared/ReactTypes'; import {REACT_LAZY_TYPE} from 'shared/ReactSymbols'; -import {disableDefaultPropsExceptForClasses} from 'shared/ReactFeatureFlags'; const Uninitialized = -1; const Pending = 0; @@ -141,35 +140,5 @@ export function lazy( _init: lazyInitializer, }; - if (!disableDefaultPropsExceptForClasses) { - if (__DEV__) { - // In production, this would just set it on the object. - let defaultProps; - // $FlowFixMe[prop-missing] - Object.defineProperties(lazyType, { - defaultProps: { - configurable: true, - get() { - return defaultProps; - }, - // $FlowFixMe[missing-local-annot] - set(newDefaultProps) { - console.error( - 'It is not supported to assign `defaultProps` to ' + - 'a lazy component import. Either specify them where the component ' + - 'is defined, or create a wrapping component around it.', - ); - defaultProps = newDefaultProps; - // Match production behavior more closely: - // $FlowFixMe[prop-missing] - Object.defineProperty(lazyType, 'defaultProps', { - enumerable: true, - }); - }, - }, - }); - } - } - return lazyType; } diff --git a/packages/react/src/__tests__/ReactElementClone-test.js b/packages/react/src/__tests__/ReactElementClone-test.js index b2e791f8268c9..81da2a6f3942a 100644 --- a/packages/react/src/__tests__/ReactElementClone-test.js +++ b/packages/react/src/__tests__/ReactElementClone-test.js @@ -291,28 +291,6 @@ describe('ReactElementClone', () => { ); }); - // @gate !disableDefaultPropsExceptForClasses - it('should normalize props with default values', () => { - class Component extends React.Component { - render() { - return ; - } - } - Component.defaultProps = {prop: 'testKey'}; - - const instance = React.createElement(Component); - const clonedInstance = React.cloneElement(instance, {prop: undefined}); - expect(clonedInstance.props.prop).toBe('testKey'); - const clonedInstance2 = React.cloneElement(instance, {prop: null}); - expect(clonedInstance2.props.prop).toBe(null); - - const instance2 = React.createElement(Component, {prop: 'newTestKey'}); - const cloneInstance3 = React.cloneElement(instance2, {prop: undefined}); - expect(cloneInstance3.props.prop).toBe('testKey'); - const cloneInstance4 = React.cloneElement(instance2, {}); - expect(cloneInstance4.props.prop).toBe('newTestKey'); - }); - it('warns for keys for arrays of elements in rest args', async () => { const root = ReactDOMClient.createRoot(document.createElement('div')); await act(() => { diff --git a/packages/react/src/__tests__/forwardRef-test.js b/packages/react/src/__tests__/forwardRef-test.js index aa151fc6d1c81..b163ac0025e5f 100644 --- a/packages/react/src/__tests__/forwardRef-test.js +++ b/packages/react/src/__tests__/forwardRef-test.js @@ -76,45 +76,6 @@ describe('forwardRef', () => { expect(ref.current).toBe(null); }); - // @gate !disableDefaultPropsExceptForClasses - it('should support defaultProps', async () => { - function FunctionComponent({forwardedRef, optional, required}) { - return ( -
- {optional} - {required} -
- ); - } - - const RefForwardingComponent = React.forwardRef( - function NamedFunction(props, ref) { - return ; - }, - ); - RefForwardingComponent.defaultProps = { - optional: 'default', - }; - - const ref = React.createRef(); - - ReactNoop.render( - , - ); - await waitForAll([]); - expect(ref.current.children).toEqual([ - {text: 'foo', hidden: false}, - {text: 'bar', hidden: false}, - ]); - - ReactNoop.render(); - await waitForAll([]); - expect(ref.current.children).toEqual([ - {text: 'default', hidden: false}, - {text: 'foo', hidden: false}, - ]); - }); - it('should warn if not provided a callback during creation', () => { React.forwardRef(undefined); assertConsoleErrorDev( diff --git a/packages/react/src/jsx/ReactJSXElement.js b/packages/react/src/jsx/ReactJSXElement.js index a20378a93cb4d..e5b2671a6eae9 100644 --- a/packages/react/src/jsx/ReactJSXElement.js +++ b/packages/react/src/jsx/ReactJSXElement.js @@ -16,10 +16,7 @@ import { } from 'shared/ReactSymbols'; import {checkKeyStringCoercion} from 'shared/CheckStringCoercion'; import isArray from 'shared/isArray'; -import { - disableDefaultPropsExceptForClasses, - ownerStackLimit, -} from 'shared/ReactFeatureFlags'; +import {ownerStackLimit} from 'shared/ReactFeatureFlags'; const createTask = // eslint-disable-next-line react-internal/no-production-logging @@ -351,18 +348,6 @@ export function jsxProd(type, config, maybeKey) { } } - if (!disableDefaultPropsExceptForClasses) { - // Resolve default props - if (type && type.defaultProps) { - const defaultProps = type.defaultProps; - for (const propName in defaultProps) { - if (props[propName] === undefined) { - props[propName] = defaultProps[propName]; - } - } - } - } - return ReactElement( type, key, @@ -598,18 +583,6 @@ function jsxDEVImpl( } } - if (!disableDefaultPropsExceptForClasses) { - // Resolve default props - if (type && type.defaultProps) { - const defaultProps = type.defaultProps; - for (const propName in defaultProps) { - if (props[propName] === undefined) { - props[propName] = defaultProps[propName]; - } - } - } - } - if (key) { const displayName = typeof type === 'function' @@ -817,14 +790,6 @@ export function cloneElement(element, config, children) { } // Remaining properties override existing props - let defaultProps; - if ( - !disableDefaultPropsExceptForClasses && - element.type && - element.type.defaultProps - ) { - defaultProps = element.type.defaultProps; - } for (propName in config) { if ( hasOwnProperty.call(config, propName) && @@ -843,16 +808,7 @@ export function cloneElement(element, config, children) { // backwards compatibility. !(propName === 'ref' && config.ref === undefined) ) { - if ( - !disableDefaultPropsExceptForClasses && - config[propName] === undefined && - defaultProps !== undefined - ) { - // Resolve default props - props[propName] = defaultProps[propName]; - } else { - props[propName] = config[propName]; - } + props[propName] = config[propName]; } } } diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index a56cc2582e5e0..e247562b690cc 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -37,9 +37,6 @@ export const favorSafetyOverHydrationPerf = true; // Need to remove didTimeout argument from Scheduler before landing export const disableSchedulerTimeoutInWorkLoop = false; -// TODO: Land at Meta before removing. -export const disableDefaultPropsExceptForClasses = true; - // ----------------------------------------------------------------------------- // Slated for removal in the future (significant effort) // diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 09f6b62983208..0c662271833df 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -33,7 +33,6 @@ export const { // The rest of the flags are static for better dead code elimination. export const disableClientCache = true; export const disableCommentsAsDOMContainers = true; -export const disableDefaultPropsExceptForClasses = true; export const disableInputAttributeSyncing = false; export const disableLegacyContext = false; export const disableLegacyContextForFunctionComponents = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 6893a3215b17d..ed372097cb4be 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -18,7 +18,6 @@ import typeof * as ExportsType from './ReactFeatureFlags.native-oss'; export const alwaysThrottleRetries = false; export const disableClientCache = true; export const disableCommentsAsDOMContainers = true; -export const disableDefaultPropsExceptForClasses = true; export const disableInputAttributeSyncing = false; export const disableLegacyContext = true; export const disableLegacyContextForFunctionComponents = true; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index c0a909ba5ce8a..66514e7697534 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -89,7 +89,6 @@ export const disableLegacyMode = true; export const disableLegacyContext = true; export const disableLegacyContextForFunctionComponents = true; export const enableReactTestRendererWarning = true; -export const disableDefaultPropsExceptForClasses = true; export const enableObjectFiber = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 209fbd4b8ceb0..ef912457ef8fb 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -63,8 +63,6 @@ export const enableInfiniteRenderLoopDetection = false; export const enableReactTestRendererWarning = false; export const disableLegacyMode = true; -export const disableDefaultPropsExceptForClasses = true; - export const renameElementSymbol = false; export const enableObjectFiber = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index eeccf763ca364..2f4da5916e860 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -14,7 +14,6 @@ // with the __VARIANT__ set to `true`, and once set to `false`. export const alwaysThrottleRetries = __VARIANT__; -export const disableDefaultPropsExceptForClasses = __VARIANT__; export const disableLegacyContextForFunctionComponents = __VARIANT__; export const disableSchedulerTimeoutInWorkLoop = __VARIANT__; export const enableHiddenSubtreeInsertionEffectCleanup = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 29bb652fd74ea..4acdf65a92274 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -16,7 +16,6 @@ const dynamicFeatureFlags: DynamicFeatureFlags = require('ReactFeatureFlags'); export const { alwaysThrottleRetries, - disableDefaultPropsExceptForClasses, disableLegacyContextForFunctionComponents, disableSchedulerTimeoutInWorkLoop, enableHiddenSubtreeInsertionEffectCleanup,