Validate propTypes, contextTypes, childContextTypes

who watches the watchmen?
This commit is contained in:
Marshall Roch
2013-11-27 12:00:30 -08:00
committed by Paul O’Shannessy
parent fcfe516a2e
commit 351dcfed01
4 changed files with 117 additions and 13 deletions

View File

@@ -26,6 +26,7 @@ var ReactOwner = require('ReactOwner');
var ReactPerf = require('ReactPerf');
var ReactPropTransferer = require('ReactPropTransferer');
var ReactPropTypeLocations = require('ReactPropTypeLocations');
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var ReactUpdates = require('ReactUpdates');
var invariant = require('invariant');
@@ -319,16 +320,46 @@ var RESERVED_SPEC_KEYS = {
}
},
childContextTypes: function(Constructor, childContextTypes) {
validateTypeDef(
Constructor,
childContextTypes,
ReactPropTypeLocations.childContext
);
Constructor.childContextTypes = childContextTypes;
},
contextTypes: function(Constructor, contextTypes) {
validateTypeDef(
Constructor,
contextTypes,
ReactPropTypeLocations.context
);
Constructor.contextTypes = contextTypes;
},
propTypes: function(Constructor, propTypes) {
validateTypeDef(
Constructor,
propTypes,
ReactPropTypeLocations.prop
);
Constructor.propTypes = propTypes;
}
};
function validateTypeDef(Constructor, typeDef, location) {
for (var propName in typeDef) {
if (typeDef.hasOwnProperty(propName)) {
invariant(
typeof typeDef[propName] == 'function',
'%s: %s type `%s` is invalid; it must be a function, usually from ' +
'React.PropTypes.',
Constructor.displayName || 'ReactCompositeComponent',
ReactPropTypeLocationNames[location],
propName
);
}
}
}
function validateMethodOverride(proto, name) {
var specPolicy = ReactCompositeComponentInterface[name];
@@ -790,9 +821,8 @@ var ReactCompositeComponentMixin = {
_checkPropTypes: function(propTypes, props, location) {
var componentName = this.constructor.displayName;
for (var propName in propTypes) {
var checkProp = propTypes[propName];
if (checkProp) {
checkProp(props, propName, componentName, location);
if (propTypes.hasOwnProperty(propName)) {
propTypes[propName](props, propName, componentName, location);
}
}
},

View File

@@ -0,0 +1,31 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @providesModule ReactPropTypeLocationNames
*/
"use strict";
var ReactPropTypeLocationNames = {};
if (__DEV__) {
ReactPropTypeLocationNames = {
prop: 'prop',
context: 'context',
childContext: 'child context'
};
}
module.exports = ReactPropTypeLocationNames;

View File

@@ -18,19 +18,11 @@
"use strict";
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var createObjectFrom = require('createObjectFrom');
var invariant = require('invariant');
var ReactPropTypeLocationNames = {};
if (__DEV__) {
ReactPropTypeLocationNames = {
prop: 'prop',
context: 'context',
childContext: 'child context'
};
}
/**
* Collection of methods that allow declaration and validation of props that are
* supplied to React components. Example usage:

View File

@@ -289,6 +289,57 @@ describe('ReactCompositeComponent', function() {
}).not.toThrow();
});
it('should throw on invalid prop types', function() {
expect(function() {
React.createClass({
displayName: 'Component',
propTypes: {
key: null
},
render: function() {
return <span>{this.props.key}</span>;
}
});
}).toThrow(
'Invariant Violation: Component: prop type `key` is invalid; ' +
'it must be a function, usually from React.PropTypes.'
);
});
it('should throw on invalid context types', function() {
expect(function() {
React.createClass({
displayName: 'Component',
contextTypes: {
key: null
},
render: function() {
return <span>{this.props.key}</span>;
}
});
}).toThrow(
'Invariant Violation: Component: context type `key` is invalid; ' +
'it must be a function, usually from React.PropTypes.'
);
});
it('should throw on invalid child context types', function() {
expect(function() {
React.createClass({
displayName: 'Component',
childContextTypes: {
key: null
},
render: function() {
return <span>{this.props.key}</span>;
}
});
}).toThrow(
'Invariant Violation: Component: child context type `key` is invalid; ' +
'it must be a function, usually from React.PropTypes.'
);
});
it('should not allow `forceUpdate` on unmounted components', function() {
var container = document.createElement('div');
document.documentElement.appendChild(container);