Add late class validation warnings for statics
ES6 classes won't have an early validation step. Therefore I added some extra validation later on in the process. These would've normally have been caught by createClass in classic React.
This commit is contained in:
parent
bd5a91a55c
commit
82d15c8fd5
|
@ -868,6 +868,12 @@ var ReactClass = {
|
||||||
// Initialize the defaultProps property after all mixins have been merged
|
// Initialize the defaultProps property after all mixins have been merged
|
||||||
if (Constructor.getDefaultProps) {
|
if (Constructor.getDefaultProps) {
|
||||||
Constructor.defaultProps = Constructor.getDefaultProps();
|
Constructor.defaultProps = Constructor.getDefaultProps();
|
||||||
|
if (__DEV__) {
|
||||||
|
// This is a tag to indicate that this use of getDefaultProps is ok,
|
||||||
|
// since it's used with createClass. If it's not, then it's likely a
|
||||||
|
// mistake so we'll warn you to use the static property instead.
|
||||||
|
Constructor.getDefaultProps._isReactClassApproved = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
invariant(
|
invariant(
|
||||||
|
|
|
@ -20,10 +20,12 @@
|
||||||
|
|
||||||
var ReactElement = require('ReactElement');
|
var ReactElement = require('ReactElement');
|
||||||
var ReactPropTypeLocations = require('ReactPropTypeLocations');
|
var ReactPropTypeLocations = require('ReactPropTypeLocations');
|
||||||
|
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
|
||||||
var ReactCurrentOwner = require('ReactCurrentOwner');
|
var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||||
|
|
||||||
var getIteratorFn = require('getIteratorFn');
|
var getIteratorFn = require('getIteratorFn');
|
||||||
var monitorCodeUse = require('monitorCodeUse');
|
var monitorCodeUse = require('monitorCodeUse');
|
||||||
|
var invariant = require('invariant');
|
||||||
var warning = require('warning');
|
var warning = require('warning');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -236,6 +238,16 @@ function checkPropTypes(componentName, propTypes, props, location) {
|
||||||
// fail the render phase where it didn't fail before. So we log it.
|
// fail the render phase where it didn't fail before. So we log it.
|
||||||
// After these have been cleaned up, we'll let them throw.
|
// After these have been cleaned up, we'll let them throw.
|
||||||
try {
|
try {
|
||||||
|
// This is intentionally an invariant that gets caught. It's the same
|
||||||
|
// behavior as without this statement except with a better message.
|
||||||
|
invariant(
|
||||||
|
typeof propTypes[propName] === 'function',
|
||||||
|
'%s: %s type `%s` is invalid; it must be a function, usually from ' +
|
||||||
|
'React.PropTypes.',
|
||||||
|
componentName || 'React class',
|
||||||
|
ReactPropTypeLocationNames[location],
|
||||||
|
propName
|
||||||
|
);
|
||||||
error = propTypes[propName](props, propName, componentName, location);
|
error = propTypes[propName](props, propName, componentName, location);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
error = ex;
|
error = ex;
|
||||||
|
@ -296,7 +308,15 @@ var ReactElementValidator = {
|
||||||
ReactPropTypeLocations.context
|
ReactPropTypeLocations.context
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (typeof type.getDefaultProps === 'function') {
|
||||||
|
warning(
|
||||||
|
type.getDefaultProps._isReactClassApproved,
|
||||||
|
'getDefaultProps is only used on classic React.createClass ' +
|
||||||
|
'definitions. Use a static property named `defaultProps` instead.'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ var ReactElement = require('ReactElement');
|
||||||
var ReactInstanceMap = require('ReactInstanceMap');
|
var ReactInstanceMap = require('ReactInstanceMap');
|
||||||
var ReactPerf = require('ReactPerf');
|
var ReactPerf = require('ReactPerf');
|
||||||
var ReactPropTypeLocations = require('ReactPropTypeLocations');
|
var ReactPropTypeLocations = require('ReactPropTypeLocations');
|
||||||
|
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
|
||||||
var ReactUpdates = require('ReactUpdates');
|
var ReactUpdates = require('ReactUpdates');
|
||||||
|
|
||||||
var assign = require('Object.assign');
|
var assign = require('Object.assign');
|
||||||
|
@ -515,8 +516,22 @@ var ReactCompositeComponentMixin = assign({},
|
||||||
this._instance.constructor.name;
|
this._instance.constructor.name;
|
||||||
for (var propName in propTypes) {
|
for (var propName in propTypes) {
|
||||||
if (propTypes.hasOwnProperty(propName)) {
|
if (propTypes.hasOwnProperty(propName)) {
|
||||||
var error =
|
var error;
|
||||||
propTypes[propName](props, propName, componentName, location);
|
try {
|
||||||
|
// This is intentionally an invariant that gets caught. It's the same
|
||||||
|
// behavior as without this statement except with a better message.
|
||||||
|
invariant(
|
||||||
|
typeof propTypes[propName] === 'function',
|
||||||
|
'%s: %s type `%s` is invalid; it must be a function, usually ' +
|
||||||
|
'from React.PropTypes.',
|
||||||
|
componentName || 'React class',
|
||||||
|
ReactPropTypeLocationNames[location],
|
||||||
|
propName
|
||||||
|
);
|
||||||
|
error = propTypes[propName](props, propName, componentName, location);
|
||||||
|
} catch (ex) {
|
||||||
|
error = ex;
|
||||||
|
}
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
// We may want to extend this logic for similar errors in
|
// We may want to extend this logic for similar errors in
|
||||||
// React.render calls, so I'm abstracting it away into
|
// React.render calls, so I'm abstracting it away into
|
||||||
|
|
|
@ -290,4 +290,62 @@ describe('ReactJSXElementValidator', function() {
|
||||||
expect(console.warn.calls.length).toBe(2);
|
expect(console.warn.calls.length).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should warn on invalid prop types', function() {
|
||||||
|
// Since there is no prevalidation step for ES6 classes, there is no hook
|
||||||
|
// for us to issue a warning earlier than element creation when the error
|
||||||
|
// actually occurs. Since this step is skipped in production, we should just
|
||||||
|
// warn instead of throwing for this case.
|
||||||
|
spyOn(console, 'warn');
|
||||||
|
class Component {
|
||||||
|
render() {
|
||||||
|
return <span>{this.props.prop}</span>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Component.propTypes = {
|
||||||
|
prop: null
|
||||||
|
};
|
||||||
|
ReactTestUtils.renderIntoDocument(<Component />);
|
||||||
|
expect(console.warn.calls.length).toBe(1);
|
||||||
|
expect(console.warn.calls[0].args[0]).toContain(
|
||||||
|
'Invariant Violation: Component: prop type `prop` is invalid; ' +
|
||||||
|
'it must be a function, usually from React.PropTypes.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should warn on invalid context types', function() {
|
||||||
|
spyOn(console, 'warn');
|
||||||
|
class Component {
|
||||||
|
render() {
|
||||||
|
return <span>{this.props.prop}</span>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Component.contextTypes = {
|
||||||
|
prop: null
|
||||||
|
};
|
||||||
|
ReactTestUtils.renderIntoDocument(<Component />);
|
||||||
|
expect(console.warn.calls.length).toBe(1);
|
||||||
|
expect(console.warn.calls[0].args[0]).toContain(
|
||||||
|
'Invariant Violation: Component: context type `prop` is invalid; ' +
|
||||||
|
'it must be a function, usually from React.PropTypes.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should warn if getDefaultProps is specificed on the class', function() {
|
||||||
|
spyOn(console, 'warn');
|
||||||
|
class Component {
|
||||||
|
render() {
|
||||||
|
return <span>{this.props.prop}</span>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Component.getDefaultProps = () => ({
|
||||||
|
prop: 'foo'
|
||||||
|
});
|
||||||
|
ReactTestUtils.renderIntoDocument(<Component />);
|
||||||
|
expect(console.warn.calls.length).toBe(1);
|
||||||
|
expect(console.warn.calls[0].args[0]).toContain(
|
||||||
|
'getDefaultProps is only used on classic React.createClass definitions.' +
|
||||||
|
' Use a static property named `defaultProps` instead.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue