mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Cleanup disableDefaultPropsExceptForClasses flag (#33648)
This commit is contained in:
+1
-53
@@ -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 <div>{value}</div>;
|
||||
}
|
||||
Foo.defaultProps = {
|
||||
defaultValue: 'default',
|
||||
};
|
||||
|
||||
async function fakeImport(result) {
|
||||
return {default: result};
|
||||
}
|
||||
|
||||
const LazyFoo = React.lazy(() => fakeImport(Foo));
|
||||
|
||||
const renderer = ReactTestRenderer.create(
|
||||
<Suspense fallback="Loading...">
|
||||
<LazyFoo />
|
||||
</Suspense>,
|
||||
);
|
||||
|
||||
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 () => {
|
||||
|
||||
@@ -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(<FunctionalComponent />);
|
||||
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(
|
||||
<div>
|
||||
<MemoComponent />
|
||||
</div>,
|
||||
);
|
||||
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},
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -205,27 +205,6 @@ describe('ReactFunctionComponent', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate !disableDefaultPropsExceptForClasses
|
||||
it('should support default props', async () => {
|
||||
function Child(props) {
|
||||
return <div>{props.test}</div>;
|
||||
}
|
||||
Child.defaultProps = {test: 2};
|
||||
|
||||
const container = document.createElement('div');
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
|
||||
await act(() => {
|
||||
root.render(<Child />);
|
||||
});
|
||||
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 {
|
||||
|
||||
+9
-86
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+2
-17
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyText />
|
||||
</Suspense>,
|
||||
{
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyText outer="World" />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll(['Hi World']);
|
||||
expect(root).toMatchRenderedOutput('Hi World');
|
||||
|
||||
root.update(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyText inner="Friends" />
|
||||
</Suspense>,
|
||||
);
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd inner="2" outer="2" />
|
||||
</Suspense>,
|
||||
{
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd inner={false} outer={false} />
|
||||
</Suspense>,
|
||||
);
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd inner="2" outer="2" />
|
||||
</Suspense>,
|
||||
{
|
||||
unstable_isConcurrent: true,
|
||||
},
|
||||
);
|
||||
|
||||
await waitForAll(['Loading...']);
|
||||
expect(root).not.toMatchRenderedOutput('22');
|
||||
|
||||
// Mount
|
||||
await act(() => resolveFakeImport(Add));
|
||||
|
||||
expect(root).toMatchRenderedOutput('22');
|
||||
|
||||
// Update
|
||||
root.update(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd inner={false} outer={false} />
|
||||
</Suspense>,
|
||||
);
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd inner="2" outer="2" />
|
||||
</Suspense>,
|
||||
{
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd inner={false} outer={false} />
|
||||
</Suspense>,
|
||||
);
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd inner="2" outer="2" />
|
||||
</Suspense>,
|
||||
{
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd inner={false} outer={false} />
|
||||
</Suspense>,
|
||||
);
|
||||
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 <Text text={props.text} />;
|
||||
};
|
||||
T.defaultProps = {
|
||||
text: 'Inner default text',
|
||||
};
|
||||
T = React.memo(T);
|
||||
const LazyText = lazy(() => fakeImport(T));
|
||||
const root = ReactTestRenderer.create(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyText />
|
||||
</Suspense>,
|
||||
{
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyText text={null} />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([null]);
|
||||
expect(root).toMatchRenderedOutput(null);
|
||||
});
|
||||
|
||||
it('includes lazy-loaded component in warning stack', async () => {
|
||||
const Foo = props => <div>{[<Text text="A" />, <Text text="B" />]}</div>;
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd outer={2} />
|
||||
</Suspense>,
|
||||
{
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd outer={2} />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
expect(root).toMatchRenderedOutput('4');
|
||||
|
||||
// Update
|
||||
root.update(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd outer={3} />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
expect(root).toMatchRenderedOutput('5');
|
||||
|
||||
// Update (shallowly equal)
|
||||
root.update(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd outer={3} />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
expect(root).toMatchRenderedOutput('5');
|
||||
|
||||
// Update (explicit props)
|
||||
root.update(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd outer={1} inner={1} />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
expect(root).toMatchRenderedOutput('2');
|
||||
|
||||
// Update (explicit props, shallowly equal)
|
||||
root.update(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd outer={1} inner={1} />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
expect(root).toMatchRenderedOutput('2');
|
||||
|
||||
// Update
|
||||
root.update(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd outer={1} />
|
||||
</Suspense>,
|
||||
);
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd outer={2} />
|
||||
</Suspense>,
|
||||
{
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd outer={3} />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
expect(root).toMatchRenderedOutput('5');
|
||||
|
||||
// Update
|
||||
root.update(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
expect(root).toMatchRenderedOutput('2');
|
||||
});
|
||||
|
||||
it('should error with a component stack naming the resolved component', async () => {
|
||||
let componentStackMessage;
|
||||
|
||||
|
||||
@@ -373,77 +373,6 @@ describe('memo', () => {
|
||||
expect(ReactNoop).toMatchRenderedOutput(<span prop="1!" />);
|
||||
});
|
||||
|
||||
// @gate !disableDefaultPropsExceptForClasses
|
||||
it('supports defaultProps defined on the memo() return value', async () => {
|
||||
function Counter({a, b, c, d, e}) {
|
||||
return <Text text={a + b + c + d + e} />;
|
||||
}
|
||||
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(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<Counter e={5} />
|
||||
</Suspense>,
|
||||
);
|
||||
});
|
||||
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(<span prop={15} />);
|
||||
|
||||
// Should bail out because props have not changed
|
||||
ReactNoop.render(
|
||||
<Suspense>
|
||||
<Counter e={5} />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
expect(ReactNoop).toMatchRenderedOutput(<span prop={15} />);
|
||||
|
||||
// Should update because count prop changed
|
||||
ReactNoop.render(
|
||||
<Suspense>
|
||||
<Counter e={10} />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([20]);
|
||||
expect(ReactNoop).toMatchRenderedOutput(<span prop={20} />);
|
||||
});
|
||||
|
||||
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(
|
||||
<div>
|
||||
<Outer />
|
||||
</div>,
|
||||
);
|
||||
});
|
||||
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(<div>111</div>);
|
||||
|
||||
await act(async () => {
|
||||
root.render(
|
||||
<div>
|
||||
<Outer inner="2" middle="3" outer="4" />
|
||||
</div>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
});
|
||||
expect(root).toMatchRenderedOutput(<div>234</div>);
|
||||
});
|
||||
|
||||
it('does not drop lower priority state updates when bailing out at higher pri (simple)', async () => {
|
||||
const {useState} = React;
|
||||
|
||||
|
||||
+3
-57
@@ -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(
|
||||
|
||||
@@ -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<T>(
|
||||
_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;
|
||||
}
|
||||
|
||||
@@ -291,28 +291,6 @@ describe('ReactElementClone', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// @gate !disableDefaultPropsExceptForClasses
|
||||
it('should normalize props with default values', () => {
|
||||
class Component extends React.Component {
|
||||
render() {
|
||||
return <span />;
|
||||
}
|
||||
}
|
||||
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(() => {
|
||||
|
||||
@@ -76,45 +76,6 @@ describe('forwardRef', () => {
|
||||
expect(ref.current).toBe(null);
|
||||
});
|
||||
|
||||
// @gate !disableDefaultPropsExceptForClasses
|
||||
it('should support defaultProps', async () => {
|
||||
function FunctionComponent({forwardedRef, optional, required}) {
|
||||
return (
|
||||
<div ref={forwardedRef}>
|
||||
{optional}
|
||||
{required}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const RefForwardingComponent = React.forwardRef(
|
||||
function NamedFunction(props, ref) {
|
||||
return <FunctionComponent {...props} forwardedRef={ref} />;
|
||||
},
|
||||
);
|
||||
RefForwardingComponent.defaultProps = {
|
||||
optional: 'default',
|
||||
};
|
||||
|
||||
const ref = React.createRef();
|
||||
|
||||
ReactNoop.render(
|
||||
<RefForwardingComponent ref={ref} optional="foo" required="bar" />,
|
||||
);
|
||||
await waitForAll([]);
|
||||
expect(ref.current.children).toEqual([
|
||||
{text: 'foo', hidden: false},
|
||||
{text: 'bar', hidden: false},
|
||||
]);
|
||||
|
||||
ReactNoop.render(<RefForwardingComponent ref={ref} required="foo" />);
|
||||
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(
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
//
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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__;
|
||||
|
||||
@@ -16,7 +16,6 @@ const dynamicFeatureFlags: DynamicFeatureFlags = require('ReactFeatureFlags');
|
||||
|
||||
export const {
|
||||
alwaysThrottleRetries,
|
||||
disableDefaultPropsExceptForClasses,
|
||||
disableLegacyContextForFunctionComponents,
|
||||
disableSchedulerTimeoutInWorkLoop,
|
||||
enableHiddenSubtreeInsertionEffectCleanup,
|
||||
|
||||
Reference in New Issue
Block a user