mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Remove non-JSX propTypes checks (#28326)
Removes all `propTypes` validation called from outside the JSX factories. Haven't touched JSX. Tests that verify related behavior are stripped down to the non-`propTypes` logic.
This commit is contained in:
135
packages/react-reconciler/src/ReactFiberBeginWork.js
vendored
135
packages/react-reconciler/src/ReactFiberBeginWork.js
vendored
@@ -40,7 +40,6 @@ import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent';
|
||||
import type {TransitionStatus} from './ReactFiberConfig';
|
||||
import type {Hook} from './ReactFiberHooks';
|
||||
|
||||
import checkPropTypes from 'shared/checkPropTypes';
|
||||
import {
|
||||
markComponentRenderStarted,
|
||||
markComponentRenderStopped,
|
||||
@@ -401,23 +400,6 @@ function updateForwardRef(
|
||||
// TODO: current can be non-null here even if the component
|
||||
// hasn't yet mounted. This happens after the first render suspends.
|
||||
// We'll need to figure out if this is fine or can cause issues.
|
||||
|
||||
if (__DEV__) {
|
||||
if (workInProgress.type !== workInProgress.elementType) {
|
||||
// Lazy component props can't be validated in createElement
|
||||
// because they're only guaranteed to be resolved here.
|
||||
const innerPropTypes = Component.propTypes;
|
||||
if (innerPropTypes) {
|
||||
checkPropTypes(
|
||||
innerPropTypes,
|
||||
nextProps, // Resolved props
|
||||
'prop',
|
||||
getComponentNameFromType(Component),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const render = Component.render;
|
||||
const ref = workInProgress.ref;
|
||||
|
||||
@@ -507,17 +489,6 @@ function updateMemoComponent(
|
||||
);
|
||||
}
|
||||
if (__DEV__) {
|
||||
const innerPropTypes = type.propTypes;
|
||||
if (innerPropTypes) {
|
||||
// Inner memo component props aren't currently validated in createElement.
|
||||
// We could move it there, but we'd still need this for lazy code path.
|
||||
checkPropTypes(
|
||||
innerPropTypes,
|
||||
nextProps, // Resolved props
|
||||
'prop',
|
||||
getComponentNameFromType(type),
|
||||
);
|
||||
}
|
||||
if (Component.defaultProps !== undefined) {
|
||||
const componentName = getComponentNameFromType(type) || 'Unknown';
|
||||
if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) {
|
||||
@@ -543,20 +514,6 @@ function updateMemoComponent(
|
||||
workInProgress.child = child;
|
||||
return child;
|
||||
}
|
||||
if (__DEV__) {
|
||||
const type = Component.type;
|
||||
const innerPropTypes = type.propTypes;
|
||||
if (innerPropTypes) {
|
||||
// Inner memo component props aren't currently validated in createElement.
|
||||
// We could move it there, but we'd still need this for lazy code path.
|
||||
checkPropTypes(
|
||||
innerPropTypes,
|
||||
nextProps, // Resolved props
|
||||
'prop',
|
||||
getComponentNameFromType(type),
|
||||
);
|
||||
}
|
||||
}
|
||||
const currentChild = ((current.child: any): Fiber); // This is always exactly one child
|
||||
const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
|
||||
current,
|
||||
@@ -592,37 +549,6 @@ function updateSimpleMemoComponent(
|
||||
// TODO: current can be non-null here even if the component
|
||||
// hasn't yet mounted. This happens when the inner render suspends.
|
||||
// We'll need to figure out if this is fine or can cause issues.
|
||||
|
||||
if (__DEV__) {
|
||||
if (workInProgress.type !== workInProgress.elementType) {
|
||||
// Lazy component props can't be validated in createElement
|
||||
// because they're only guaranteed to be resolved here.
|
||||
let outerMemoType = workInProgress.elementType;
|
||||
if (outerMemoType.$$typeof === REACT_LAZY_TYPE) {
|
||||
// We warn when you define propTypes on lazy()
|
||||
// so let's just skip over it to find memo() outer wrapper.
|
||||
// Inner props for memo are validated later.
|
||||
const lazyComponent: LazyComponentType<any, any> = outerMemoType;
|
||||
const payload = lazyComponent._payload;
|
||||
const init = lazyComponent._init;
|
||||
try {
|
||||
outerMemoType = init(payload);
|
||||
} catch (x) {
|
||||
outerMemoType = null;
|
||||
}
|
||||
// Inner propTypes will be validated in the function component path.
|
||||
const outerPropTypes = outerMemoType && (outerMemoType: any).propTypes;
|
||||
if (outerPropTypes) {
|
||||
checkPropTypes(
|
||||
outerPropTypes,
|
||||
nextProps, // Resolved (SimpleMemoComponent has no defaultProps)
|
||||
'prop',
|
||||
getComponentNameFromType(outerMemoType),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (current !== null) {
|
||||
const prevProps = current.memoizedProps;
|
||||
if (
|
||||
@@ -1099,22 +1025,6 @@ function updateFunctionComponent(
|
||||
nextProps: any,
|
||||
renderLanes: Lanes,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
if (workInProgress.type !== workInProgress.elementType) {
|
||||
// Lazy component props can't be validated in createElement
|
||||
// because they're only guaranteed to be resolved here.
|
||||
const innerPropTypes = Component.propTypes;
|
||||
if (innerPropTypes) {
|
||||
checkPropTypes(
|
||||
innerPropTypes,
|
||||
nextProps, // Resolved props
|
||||
'prop',
|
||||
getComponentNameFromType(Component),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let context;
|
||||
if (!disableLegacyContext) {
|
||||
const unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
|
||||
@@ -1253,20 +1163,6 @@ function updateClassComponent(
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (workInProgress.type !== workInProgress.elementType) {
|
||||
// Lazy component props can't be validated in createElement
|
||||
// because they're only guaranteed to be resolved here.
|
||||
const innerPropTypes = Component.propTypes;
|
||||
if (innerPropTypes) {
|
||||
checkPropTypes(
|
||||
innerPropTypes,
|
||||
nextProps, // Resolved props
|
||||
'prop',
|
||||
getComponentNameFromType(Component),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push context providers early to prevent context stack mismatches.
|
||||
@@ -1815,19 +1711,6 @@ function mountLazyComponent(
|
||||
return child;
|
||||
}
|
||||
case MemoComponent: {
|
||||
if (__DEV__) {
|
||||
if (workInProgress.type !== workInProgress.elementType) {
|
||||
const outerPropTypes = Component.propTypes;
|
||||
if (outerPropTypes) {
|
||||
checkPropTypes(
|
||||
outerPropTypes,
|
||||
resolvedProps, // Resolved for outer only
|
||||
'prop',
|
||||
getComponentNameFromType(Component),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
child = updateMemoComponent(
|
||||
null,
|
||||
workInProgress,
|
||||
@@ -3549,11 +3432,6 @@ function updateContextProvider(
|
||||
);
|
||||
}
|
||||
}
|
||||
const providerPropTypes = workInProgress.type.propTypes;
|
||||
|
||||
if (providerPropTypes) {
|
||||
checkPropTypes(providerPropTypes, newProps, 'prop', 'Context.Provider');
|
||||
}
|
||||
}
|
||||
|
||||
pushProvider(workInProgress, context, newValue);
|
||||
@@ -4229,19 +4107,6 @@ function beginWork(
|
||||
const unresolvedProps = workInProgress.pendingProps;
|
||||
// Resolve outer props first, then resolve inner props.
|
||||
let resolvedProps = resolveDefaultProps(type, unresolvedProps);
|
||||
if (__DEV__) {
|
||||
if (workInProgress.type !== workInProgress.elementType) {
|
||||
const outerPropTypes = type.propTypes;
|
||||
if (outerPropTypes) {
|
||||
checkPropTypes(
|
||||
outerPropTypes,
|
||||
resolvedProps, // Resolved for outer only
|
||||
'prop',
|
||||
getComponentNameFromType(type),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
resolvedProps = resolveDefaultProps(type.type, resolvedProps);
|
||||
return updateMemoComponent(
|
||||
current,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
let PropTypes;
|
||||
let React;
|
||||
let ReactTestRenderer;
|
||||
let Scheduler;
|
||||
@@ -28,7 +27,6 @@ describe('ReactLazy', () => {
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
|
||||
ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||
PropTypes = require('prop-types');
|
||||
React = require('react');
|
||||
Suspense = React.Suspense;
|
||||
lazy = React.lazy;
|
||||
@@ -783,33 +781,12 @@ describe('ReactLazy', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('warns about defining propTypes on the outer wrapper', () => {
|
||||
const LazyText = lazy(() => fakeImport(Text));
|
||||
expect(() => {
|
||||
LazyText.propTypes = {hello: () => {}};
|
||||
}).toErrorDev(
|
||||
'React.lazy(...): It is not supported to assign `propTypes` to ' +
|
||||
'a lazy component import. Either specify them where the component ' +
|
||||
'is defined, or create a wrapping component around it.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
});
|
||||
|
||||
async function verifyInnerPropTypesAreChecked(
|
||||
async function verifyResolvesProps(
|
||||
Add,
|
||||
shouldWarnAboutFunctionDefaultProps,
|
||||
shouldWarnAboutMemoDefaultProps,
|
||||
) {
|
||||
const LazyAdd = lazy(() => fakeImport(Add));
|
||||
expect(() => {
|
||||
LazyAdd.propTypes = {};
|
||||
}).toErrorDev(
|
||||
'React.lazy(...): It is not supported to assign `propTypes` 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..." />}>
|
||||
<LazyAdd inner="2" outer="2" />
|
||||
@@ -820,7 +797,6 @@ describe('ReactLazy', () => {
|
||||
);
|
||||
|
||||
await waitForAll(['Loading...']);
|
||||
|
||||
expect(root).not.toMatchRenderedOutput('22');
|
||||
|
||||
// Mount
|
||||
@@ -830,171 +806,126 @@ describe('ReactLazy', () => {
|
||||
shouldWarnAboutFunctionDefaultProps
|
||||
? [
|
||||
'Add: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
|
||||
'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.',
|
||||
]
|
||||
: shouldWarnAboutMemoDefaultProps
|
||||
? [
|
||||
'Add: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.',
|
||||
]
|
||||
: [
|
||||
'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.',
|
||||
],
|
||||
: [],
|
||||
);
|
||||
expect(root).toMatchRenderedOutput('22');
|
||||
|
||||
// Update
|
||||
await expect(async () => {
|
||||
root.update(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd inner={false} outer={false} />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
}).toErrorDev(
|
||||
'Invalid prop `inner` of type `boolean` supplied to `Add`, expected `number`.',
|
||||
root.update(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyAdd inner={false} outer={false} />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
expect(root).toMatchRenderedOutput('0');
|
||||
}
|
||||
|
||||
// Note: all "with defaultProps" tests below also verify defaultProps works as expected.
|
||||
// If we ever delete or move propTypes-related tests, make sure not to delete these.
|
||||
it('respects propTypes on function component with defaultProps', async () => {
|
||||
it('resolves props for function component with defaultProps', async () => {
|
||||
function Add(props) {
|
||||
expect(props.innerWithDefault).toBe(42);
|
||||
return props.inner + props.outer;
|
||||
}
|
||||
Add.propTypes = {
|
||||
inner: PropTypes.number.isRequired,
|
||||
innerWithDefault: PropTypes.number.isRequired,
|
||||
};
|
||||
Add.defaultProps = {
|
||||
innerWithDefault: 42,
|
||||
};
|
||||
await verifyInnerPropTypesAreChecked(Add, true);
|
||||
await verifyResolvesProps(Add, true);
|
||||
});
|
||||
|
||||
it('respects propTypes on function component without defaultProps', async () => {
|
||||
it('resolves props for function component without defaultProps', async () => {
|
||||
function Add(props) {
|
||||
return props.inner + props.outer;
|
||||
}
|
||||
Add.propTypes = {
|
||||
inner: PropTypes.number.isRequired,
|
||||
};
|
||||
await verifyInnerPropTypesAreChecked(Add);
|
||||
await verifyResolvesProps(Add);
|
||||
});
|
||||
|
||||
it('respects propTypes on class component with defaultProps', async () => {
|
||||
it('resolves props for class component with defaultProps', async () => {
|
||||
class Add extends React.Component {
|
||||
render() {
|
||||
expect(this.props.innerWithDefault).toBe(42);
|
||||
return this.props.inner + this.props.outer;
|
||||
}
|
||||
}
|
||||
Add.propTypes = {
|
||||
inner: PropTypes.number.isRequired,
|
||||
innerWithDefault: PropTypes.number.isRequired,
|
||||
};
|
||||
Add.defaultProps = {
|
||||
innerWithDefault: 42,
|
||||
};
|
||||
await verifyInnerPropTypesAreChecked(Add);
|
||||
await verifyResolvesProps(Add);
|
||||
});
|
||||
|
||||
it('respects propTypes on class component without defaultProps', async () => {
|
||||
it('resolves props for class component without defaultProps', async () => {
|
||||
class Add extends React.Component {
|
||||
render() {
|
||||
return this.props.inner + this.props.outer;
|
||||
}
|
||||
}
|
||||
Add.propTypes = {
|
||||
inner: PropTypes.number.isRequired,
|
||||
};
|
||||
await verifyInnerPropTypesAreChecked(Add);
|
||||
await verifyResolvesProps(Add);
|
||||
});
|
||||
|
||||
it('respects propTypes on forwardRef component with defaultProps', async () => {
|
||||
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.propTypes = {
|
||||
inner: PropTypes.number.isRequired,
|
||||
innerWithDefault: PropTypes.number.isRequired,
|
||||
};
|
||||
Add.defaultProps = {
|
||||
innerWithDefault: 42,
|
||||
};
|
||||
await verifyInnerPropTypesAreChecked(Add);
|
||||
await verifyResolvesProps(Add);
|
||||
});
|
||||
|
||||
it('respects propTypes on forwardRef component without defaultProps', async () => {
|
||||
it('resolves props for forwardRef component without defaultProps', async () => {
|
||||
const Add = React.forwardRef((props, ref) => {
|
||||
return props.inner + props.outer;
|
||||
});
|
||||
Add.displayName = 'Add';
|
||||
Add.propTypes = {
|
||||
inner: PropTypes.number.isRequired,
|
||||
};
|
||||
await verifyInnerPropTypesAreChecked(Add);
|
||||
await verifyResolvesProps(Add);
|
||||
});
|
||||
|
||||
it('respects propTypes on outer memo component with defaultProps', async () => {
|
||||
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.propTypes = {
|
||||
inner: PropTypes.number.isRequired,
|
||||
innerWithDefault: PropTypes.number.isRequired,
|
||||
};
|
||||
Add.defaultProps = {
|
||||
innerWithDefault: 42,
|
||||
};
|
||||
await verifyInnerPropTypesAreChecked(Add, false, true);
|
||||
await verifyResolvesProps(Add, false, true);
|
||||
});
|
||||
|
||||
it('respects propTypes on outer memo component without defaultProps', async () => {
|
||||
it('resolves props for outer memo component without defaultProps', async () => {
|
||||
let Add = props => {
|
||||
return props.inner + props.outer;
|
||||
};
|
||||
Add = React.memo(Add);
|
||||
Add.propTypes = {
|
||||
inner: PropTypes.number.isRequired,
|
||||
};
|
||||
await verifyInnerPropTypesAreChecked(Add);
|
||||
await verifyResolvesProps(Add);
|
||||
});
|
||||
|
||||
it('respects propTypes on inner memo component with defaultProps', async () => {
|
||||
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.propTypes = {
|
||||
inner: PropTypes.number.isRequired,
|
||||
innerWithDefault: PropTypes.number.isRequired,
|
||||
};
|
||||
Add.defaultProps = {
|
||||
innerWithDefault: 42,
|
||||
};
|
||||
await verifyInnerPropTypesAreChecked(React.memo(Add), true);
|
||||
await verifyResolvesProps(React.memo(Add), true);
|
||||
});
|
||||
|
||||
it('respects propTypes on inner memo component without defaultProps', async () => {
|
||||
it('resolves props for inner memo component without defaultProps', async () => {
|
||||
const Add = props => {
|
||||
return props.inner + props.outer;
|
||||
};
|
||||
Add.displayName = 'Add';
|
||||
Add.propTypes = {
|
||||
inner: PropTypes.number.isRequired,
|
||||
};
|
||||
await verifyInnerPropTypesAreChecked(React.memo(Add));
|
||||
await verifyResolvesProps(React.memo(Add));
|
||||
});
|
||||
|
||||
it('uses outer resolved props for validating propTypes on memo', async () => {
|
||||
it('uses outer resolved props on memo', async () => {
|
||||
let T = props => {
|
||||
return <Text text={props.text} />;
|
||||
};
|
||||
@@ -1002,10 +933,6 @@ describe('ReactLazy', () => {
|
||||
text: 'Inner default text',
|
||||
};
|
||||
T = React.memo(T);
|
||||
T.propTypes = {
|
||||
// Should not be satisfied by the *inner* defaultProps.
|
||||
text: PropTypes.string.isRequired,
|
||||
};
|
||||
const LazyText = lazy(() => fakeImport(T));
|
||||
const root = ReactTestRenderer.create(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
@@ -1025,21 +952,16 @@ describe('ReactLazy', () => {
|
||||
assertLog(['Inner default text']);
|
||||
}).toErrorDev([
|
||||
'T: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
|
||||
'The prop `text` is marked as required in `T`, but its value is `undefined`',
|
||||
]);
|
||||
expect(root).toMatchRenderedOutput('Inner default text');
|
||||
|
||||
// Update
|
||||
await expect(async () => {
|
||||
root.update(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyText text={null} />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([null]);
|
||||
}).toErrorDev(
|
||||
'The prop `text` is marked as required in `T`, but its value is `null`',
|
||||
root.update(
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyText text={null} />
|
||||
</Suspense>,
|
||||
);
|
||||
await waitForAll([null]);
|
||||
expect(root).toMatchRenderedOutput(null);
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
let PropTypes;
|
||||
let React;
|
||||
let ReactNoop;
|
||||
let Suspense;
|
||||
@@ -25,7 +24,6 @@ describe('memo', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
PropTypes = require('prop-types');
|
||||
React = require('react');
|
||||
ReactNoop = require('react-noop-renderer');
|
||||
Scheduler = require('scheduler');
|
||||
@@ -483,108 +481,39 @@ describe('memo', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('validates propTypes declared on the inner component', async () => {
|
||||
function FnInner(props) {
|
||||
return props.inner;
|
||||
}
|
||||
FnInner.propTypes = {inner: PropTypes.number.isRequired};
|
||||
const Fn = React.memo(FnInner);
|
||||
|
||||
// Mount
|
||||
await expect(async () => {
|
||||
ReactNoop.render(<Fn inner="2" />);
|
||||
await waitForAll([]);
|
||||
}).toErrorDev(
|
||||
'Invalid prop `inner` of type `string` supplied to `FnInner`, expected `number`.',
|
||||
);
|
||||
|
||||
// Update
|
||||
await expect(async () => {
|
||||
ReactNoop.render(<Fn inner={false} />);
|
||||
await waitForAll([]);
|
||||
}).toErrorDev(
|
||||
'Invalid prop `inner` of type `boolean` supplied to `FnInner`, expected `number`.',
|
||||
);
|
||||
});
|
||||
|
||||
it('validates propTypes declared on the outer component', async () => {
|
||||
function FnInner(props) {
|
||||
return props.outer;
|
||||
}
|
||||
const Fn = React.memo(FnInner);
|
||||
Fn.propTypes = {outer: PropTypes.number.isRequired};
|
||||
|
||||
// Mount
|
||||
await expect(async () => {
|
||||
ReactNoop.render(<Fn outer="3" />);
|
||||
await waitForAll([]);
|
||||
}).toErrorDev(
|
||||
// Outer props are checked in createElement
|
||||
'Invalid prop `outer` of type `string` supplied to `FnInner`, expected `number`.',
|
||||
);
|
||||
|
||||
// Update
|
||||
await expect(async () => {
|
||||
ReactNoop.render(<Fn outer={false} />);
|
||||
await waitForAll([]);
|
||||
}).toErrorDev(
|
||||
// Outer props are checked in createElement
|
||||
'Invalid prop `outer` of type `boolean` supplied to `FnInner`, expected `number`.',
|
||||
);
|
||||
});
|
||||
|
||||
it('validates nested propTypes declarations', async () => {
|
||||
it('handles nested defaultProps declarations', async () => {
|
||||
function Inner(props) {
|
||||
return props.inner + props.middle + props.outer;
|
||||
}
|
||||
Inner.propTypes = {inner: PropTypes.number.isRequired};
|
||||
Inner.defaultProps = {inner: 0};
|
||||
Inner.defaultProps = {inner: 1};
|
||||
const Middle = React.memo(Inner);
|
||||
Middle.propTypes = {middle: PropTypes.number.isRequired};
|
||||
Middle.defaultProps = {middle: 0};
|
||||
Middle.defaultProps = {middle: 10};
|
||||
const Outer = React.memo(Middle);
|
||||
Outer.propTypes = {outer: PropTypes.number.isRequired};
|
||||
Outer.defaultProps = {outer: 0};
|
||||
Outer.defaultProps = {outer: 100};
|
||||
|
||||
// No warning expected because defaultProps satisfy both.
|
||||
ReactNoop.render(
|
||||
<div>
|
||||
<Outer />
|
||||
</div>,
|
||||
);
|
||||
const root = ReactNoop.createRoot();
|
||||
await expect(async () => {
|
||||
await waitForAll([]);
|
||||
await act(() => {
|
||||
root.render(
|
||||
<div>
|
||||
<Outer />
|
||||
</div>,
|
||||
);
|
||||
});
|
||||
}).toErrorDev([
|
||||
'Inner: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
'Support for defaultProps will be removed from memo component',
|
||||
]);
|
||||
expect(root).toMatchRenderedOutput(<div>111</div>);
|
||||
|
||||
// Mount
|
||||
await expect(async () => {
|
||||
ReactNoop.render(
|
||||
await act(async () => {
|
||||
root.render(
|
||||
<div>
|
||||
<Outer inner="2" middle="3" outer="4" />
|
||||
</div>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
}).toErrorDev([
|
||||
'Invalid prop `outer` of type `string` supplied to `Inner`, expected `number`.',
|
||||
'Invalid prop `middle` of type `string` supplied to `Inner`, expected `number`.',
|
||||
'Invalid prop `inner` of type `string` supplied to `Inner`, expected `number`.',
|
||||
]);
|
||||
|
||||
// Update
|
||||
await expect(async () => {
|
||||
ReactNoop.render(
|
||||
<div>
|
||||
<Outer inner={false} middle={false} outer={false} />
|
||||
</div>,
|
||||
);
|
||||
await waitForAll([]);
|
||||
}).toErrorDev([
|
||||
'Invalid prop `outer` of type `boolean` supplied to `Inner`, expected `number`.',
|
||||
'Invalid prop `middle` of type `boolean` supplied to `Inner`, expected `number`.',
|
||||
'Invalid prop `inner` of type `boolean` supplied to `Inner`, expected `number`.',
|
||||
]);
|
||||
});
|
||||
expect(root).toMatchRenderedOutput(<div>234</div>);
|
||||
});
|
||||
|
||||
it('does not drop lower priority state updates when bailing out at higher pri (simple)', async () => {
|
||||
|
||||
11
packages/react-server/src/ReactFizzContext.js
vendored
11
packages/react-server/src/ReactFizzContext.js
vendored
@@ -9,7 +9,6 @@
|
||||
|
||||
import {disableLegacyContext} from 'shared/ReactFeatureFlags';
|
||||
import getComponentNameFromType from 'shared/getComponentNameFromType';
|
||||
import checkPropTypes from 'shared/checkPropTypes';
|
||||
|
||||
let warnedAboutMissingGetChildContext;
|
||||
|
||||
@@ -36,11 +35,6 @@ export function getMaskedContext(type: any, unmaskedContext: Object): Object {
|
||||
context[key] = unmaskedContext[key];
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
const name = getComponentNameFromType(type) || 'Unknown';
|
||||
checkPropTypes(contextTypes, context, 'context', name);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
||||
@@ -84,11 +78,6 @@ export function processChildContext(
|
||||
);
|
||||
}
|
||||
}
|
||||
if (__DEV__) {
|
||||
const name = getComponentNameFromType(type) || 'Unknown';
|
||||
checkPropTypes(childContextTypes, childContext, 'child context', name);
|
||||
}
|
||||
|
||||
return {...parentContext, ...childContext};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,9 @@ export function forwardRef<Props, ElementType: React$ElementType>(
|
||||
}
|
||||
|
||||
if (render != null) {
|
||||
if (render.defaultProps != null || render.propTypes != null) {
|
||||
if (render.defaultProps != null) {
|
||||
console.error(
|
||||
'forwardRef render functions do not support propTypes or defaultProps. ' +
|
||||
'forwardRef render functions do not support defaultProps. ' +
|
||||
'Did you accidentally pass a React component?',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,14 +10,12 @@
|
||||
'use strict';
|
||||
|
||||
describe('forwardRef', () => {
|
||||
let PropTypes;
|
||||
let React;
|
||||
let ReactNoop;
|
||||
let waitForAll;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
PropTypes = require('prop-types');
|
||||
React = require('react');
|
||||
ReactNoop = require('react-noop-renderer');
|
||||
|
||||
@@ -76,7 +74,7 @@ describe('forwardRef', () => {
|
||||
expect(ref.current).toBe(null);
|
||||
});
|
||||
|
||||
it('should support propTypes and defaultProps', async () => {
|
||||
it('should support defaultProps', async () => {
|
||||
function FunctionComponent({forwardedRef, optional, required}) {
|
||||
return (
|
||||
<div ref={forwardedRef}>
|
||||
@@ -91,10 +89,6 @@ describe('forwardRef', () => {
|
||||
return <FunctionComponent {...props} forwardedRef={ref} />;
|
||||
},
|
||||
);
|
||||
RefForwardingComponent.propTypes = {
|
||||
optional: PropTypes.string,
|
||||
required: PropTypes.string.isRequired,
|
||||
};
|
||||
RefForwardingComponent.defaultProps = {
|
||||
optional: 'default',
|
||||
};
|
||||
@@ -116,14 +110,6 @@ describe('forwardRef', () => {
|
||||
{text: 'default', hidden: false},
|
||||
{text: 'foo', hidden: false},
|
||||
]);
|
||||
|
||||
expect(() =>
|
||||
ReactNoop.render(<RefForwardingComponent ref={ref} optional="foo" />),
|
||||
).toErrorDev(
|
||||
'Warning: Failed prop type: The prop `required` is marked as required in ' +
|
||||
'`ForwardRef(NamedFunction)`, but its value is `undefined`.\n' +
|
||||
' in NamedFunction (at **)',
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn if not provided a callback during creation', () => {
|
||||
@@ -150,24 +136,14 @@ describe('forwardRef', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn if the render function provided has propTypes or defaultProps attributes', () => {
|
||||
function renderWithPropTypes(props, ref) {
|
||||
return null;
|
||||
}
|
||||
renderWithPropTypes.propTypes = {};
|
||||
|
||||
it('should warn if the render function provided has defaultProps attributes', () => {
|
||||
function renderWithDefaultProps(props, ref) {
|
||||
return null;
|
||||
}
|
||||
renderWithDefaultProps.defaultProps = {};
|
||||
|
||||
expect(() => React.forwardRef(renderWithPropTypes)).toErrorDev(
|
||||
'forwardRef render functions do not support propTypes or defaultProps. ' +
|
||||
'Did you accidentally pass a React component?',
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(() => React.forwardRef(renderWithDefaultProps)).toErrorDev(
|
||||
'forwardRef render functions do not support propTypes or defaultProps. ' +
|
||||
'forwardRef render functions do not support defaultProps. ' +
|
||||
'Did you accidentally pass a React component?',
|
||||
{withoutStack: true},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user