mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Validate memo() inner propTypes and warn about shadowing
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
let PropTypes;
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
let ReactNoop;
|
||||
@@ -22,6 +23,7 @@ describe('memo', () => {
|
||||
jest.resetModules();
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
|
||||
PropTypes = require('prop-types');
|
||||
React = require('react');
|
||||
ReactNoop = require('react-noop-renderer');
|
||||
({Suspense} = React);
|
||||
@@ -321,6 +323,58 @@ describe('memo', () => {
|
||||
{withoutStack: true},
|
||||
);
|
||||
});
|
||||
|
||||
it('validates propTypes declared on the inner component', () => {
|
||||
function FnInner(props) {
|
||||
return props.inner + props.outer;
|
||||
}
|
||||
FnInner.propTypes = {inner: PropTypes.number.isRequired};
|
||||
const Fn = React.memo(FnInner);
|
||||
expect(() => {
|
||||
ReactNoop.render(<Fn inner="2" outer="3" />);
|
||||
}).toWarnDev(
|
||||
'Invalid prop `inner` of type `string` supplied to `FnInner`, expected `number`.\n' +
|
||||
' in FnInner (at **)',
|
||||
);
|
||||
});
|
||||
|
||||
it('validates propTypes declared on the outer component', () => {
|
||||
function FnInner(props) {
|
||||
return props.inner + props.outer;
|
||||
}
|
||||
const Fn = React.memo(FnInner);
|
||||
Fn.propTypes = {outer: PropTypes.number.isRequired};
|
||||
expect(() => {
|
||||
ReactNoop.render(<Fn inner="2" outer="3" />);
|
||||
}).toWarnDev(
|
||||
'Invalid prop `outer` of type `string` supplied to `FnInner`, expected `number`.\n' +
|
||||
' in FnInner (at **)',
|
||||
);
|
||||
});
|
||||
|
||||
it('warns about propTypes declared on both the outer and the inner component', () => {
|
||||
function FnInner(props) {
|
||||
return props.inner + props.outer;
|
||||
}
|
||||
FnInner.propTypes = {inner: PropTypes.number.isRequired};
|
||||
const Fn = React.memo(FnInner);
|
||||
Fn.propTypes = {outer: PropTypes.number.isRequired};
|
||||
expect(() => {
|
||||
ReactNoop.render(<Fn inner="2" outer="3" />);
|
||||
}).toWarnDev(
|
||||
[
|
||||
'React.memo(FnInner): `propTypes` are defined both on the `React.memo()` result ' +
|
||||
'and on the inner `FnInner` component. Remove either one of these `propTypes` definitions.',
|
||||
'Invalid prop `outer` of type `string` supplied to `FnInner`, expected `number`.\n' +
|
||||
' in FnInner (at **)',
|
||||
// Outer propTypes shadow the inner ones and aren't validated in this case.
|
||||
],
|
||||
{withoutStack: 1},
|
||||
);
|
||||
|
||||
// Deduplication
|
||||
ReactNoop.render(<Fn inner="2" outer="3" />);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
REACT_FRAGMENT_TYPE,
|
||||
REACT_ELEMENT_TYPE,
|
||||
REACT_LAZY_TYPE,
|
||||
REACT_MEMO_TYPE,
|
||||
} from 'shared/ReactSymbols';
|
||||
import checkPropTypes from 'prop-types/checkPropTypes';
|
||||
import warning from 'shared/warning';
|
||||
@@ -34,9 +35,11 @@ import ReactDebugCurrentFrame, {
|
||||
} from './ReactDebugCurrentFrame';
|
||||
|
||||
let propTypesMisspellWarningShown;
|
||||
let didWarnAboutDuplicatePropTypes;
|
||||
|
||||
if (__DEV__) {
|
||||
propTypesMisspellWarningShown = false;
|
||||
didWarnAboutDuplicatePropTypes = {};
|
||||
}
|
||||
|
||||
function getDeclarationErrorAddendum() {
|
||||
@@ -188,6 +191,23 @@ function getPropTypes(type) {
|
||||
switch (type.$$typeof) {
|
||||
case REACT_FORWARD_REF_TYPE:
|
||||
return type.propTypes;
|
||||
case REACT_MEMO_TYPE:
|
||||
const outerPropTypes = type.propTypes;
|
||||
const innerPropTypes = type.type.propTypes;
|
||||
if (outerPropTypes && innerPropTypes) {
|
||||
const componentName = getComponentName(type);
|
||||
if (!didWarnAboutDuplicatePropTypes[componentName]) {
|
||||
didWarnAboutDuplicatePropTypes[componentName] = true;
|
||||
warning(
|
||||
false,
|
||||
'React.memo(%s): `propTypes` are defined both on the `React.memo()` result and on the ' +
|
||||
'inner `%s` component. Remove either one of these `propTypes` definitions.',
|
||||
componentName,
|
||||
componentName,
|
||||
);
|
||||
}
|
||||
}
|
||||
return outerPropTypes || innerPropTypes;
|
||||
case REACT_LAZY_TYPE:
|
||||
const resolvedType = refineResolvedLazyComponent(type);
|
||||
if (resolvedType !== null) {
|
||||
|
||||
Reference in New Issue
Block a user