@samitouri / QOS-React-2 / commits / 351dcfed01

Validate propTypes, contextTypes, childContextTypes

who watches the watchmen?

Marshall Roch committed Nov 27, 2013 at 12:00 UTC 351dcfed01d51dd18016d84ded4f86bddecaa7ba
4 files changed +117 -13
src/core/ReactCompositeComponent.js
+33 -3
@@ -26,6 +26,7 @@ var ReactOwner = require('ReactOwner');
26 var ReactPerf = require('ReactPerf');
27 var ReactPropTransferer = require('ReactPropTransferer');
28 var ReactPropTypeLocations = require('ReactPropTypeLocations');
29 +var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
30 var ReactUpdates = require('ReactUpdates');
31
32 var invariant = require('invariant');
@@ -319,16 +320,46 @@ var RESERVED_SPEC_KEYS = {
320 }
321 },
322 childContextTypes: function(Constructor, childContextTypes) {
323 + validateTypeDef(
324 + Constructor,
325 + childContextTypes,
326 + ReactPropTypeLocations.childContext
327 + );
328 Constructor.childContextTypes = childContextTypes;
329 },
330 contextTypes: function(Constructor, contextTypes) {
331 + validateTypeDef(
332 + Constructor,
333 + contextTypes,
334 + ReactPropTypeLocations.context
335 + );
336 Constructor.contextTypes = contextTypes;
337 },
338 propTypes: function(Constructor, propTypes) {
339 + validateTypeDef(
340 + Constructor,
341 + propTypes,
342 + ReactPropTypeLocations.prop
343 + );
344 Constructor.propTypes = propTypes;
345 }
346 };
347
348 +function validateTypeDef(Constructor, typeDef, location) {
349 + for (var propName in typeDef) {
350 + if (typeDef.hasOwnProperty(propName)) {
351 + invariant(
352 + typeof typeDef[propName] == 'function',
353 + '%s: %s type `%s` is invalid; it must be a function, usually from ' +
354 + 'React.PropTypes.',
355 + Constructor.displayName || 'ReactCompositeComponent',
356 + ReactPropTypeLocationNames[location],
357 + propName
358 + );
359 + }
360 + }
361 +}
362 +
363 function validateMethodOverride(proto, name) {
364 var specPolicy = ReactCompositeComponentInterface[name];
365
@@ -790,9 +821,8 @@ var ReactCompositeComponentMixin = {
821 _checkPropTypes: function(propTypes, props, location) {
822 var componentName = this.constructor.displayName;
823 for (var propName in propTypes) {
793 - var checkProp = propTypes[propName];
794 - if (checkProp) {
795 - checkProp(props, propName, componentName, location);
824 + if (propTypes.hasOwnProperty(propName)) {
825 + propTypes[propName](props, propName, componentName, location);
826 }
827 }
828 },
src/core/ReactPropTypeLocationNames.js new
+31
@@ -0,0 +1,31 @@
1 +/**
2 + * Copyright 2013 Facebook, Inc.
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + *
16 + * @providesModule ReactPropTypeLocationNames
17 + */
18 +
19 +"use strict";
20 +
21 +var ReactPropTypeLocationNames = {};
22 +
23 +if (__DEV__) {
24 + ReactPropTypeLocationNames = {
25 + prop: 'prop',
26 + context: 'context',
27 + childContext: 'child context'
28 + };
29 +}
30 +
31 +module.exports = ReactPropTypeLocationNames;
src/core/ReactPropTypes.js
+2 -10
@@ -18,19 +18,11 @@
18
19 "use strict";
20
21 +var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
22 +
23 var createObjectFrom = require('createObjectFrom');
24 var invariant = require('invariant');
25
24 -var ReactPropTypeLocationNames = {};
25 -
26 -if (__DEV__) {
27 - ReactPropTypeLocationNames = {
28 - prop: 'prop',
29 - context: 'context',
30 - childContext: 'child context'
31 - };
32 -}
33 -
26 /**
27 * Collection of methods that allow declaration and validation of props that are
28 * supplied to React components. Example usage:
src/core/__tests__/ReactCompositeComponent-test.js
+51
@@ -289,6 +289,57 @@ describe('ReactCompositeComponent', function() {
289 }).not.toThrow();
290 });
291
292 + it('should throw on invalid prop types', function() {
293 + expect(function() {
294 + React.createClass({
295 + displayName: 'Component',
296 + propTypes: {
297 + key: null
298 + },
299 + render: function() {
300 + return <span>{this.props.key}</span>;
301 + }
302 + });
303 + }).toThrow(
304 + 'Invariant Violation: Component: prop type `key` is invalid; ' +
305 + 'it must be a function, usually from React.PropTypes.'
306 + );
307 + });
308 +
309 + it('should throw on invalid context types', function() {
310 + expect(function() {
311 + React.createClass({
312 + displayName: 'Component',
313 + contextTypes: {
314 + key: null
315 + },
316 + render: function() {
317 + return <span>{this.props.key}</span>;
318 + }
319 + });
320 + }).toThrow(
321 + 'Invariant Violation: Component: context type `key` is invalid; ' +
322 + 'it must be a function, usually from React.PropTypes.'
323 + );
324 + });
325 +
326 + it('should throw on invalid child context types', function() {
327 + expect(function() {
328 + React.createClass({
329 + displayName: 'Component',
330 + childContextTypes: {
331 + key: null
332 + },
333 + render: function() {
334 + return <span>{this.props.key}</span>;
335 + }
336 + });
337 + }).toThrow(
338 + 'Invariant Violation: Component: child context type `key` is invalid; ' +
339 + 'it must be a function, usually from React.PropTypes.'
340 + );
341 + });
342 +
343 it('should not allow `forceUpdate` on unmounted components', function() {
344 var container = document.createElement('div');
345 document.documentElement.appendChild(container);