diff --git a/src/isomorphic/classic/__tests__/ReactContextValidator-test.js b/src/isomorphic/classic/__tests__/ReactContextValidator-test.js
index cc09080f0cc96..972093d8f54ad 100644
--- a/src/isomorphic/classic/__tests__/ReactContextValidator-test.js
+++ b/src/isomorphic/classic/__tests__/ReactContextValidator-test.js
@@ -24,6 +24,10 @@ var ReactTestUtils;
var reactComponentExpect;
describe('ReactContextValidator', function() {
+ function normalizeCodeLocInfo(str) {
+ return str.replace(/\(at .+?:\d+\)/g, '(at **)');
+ }
+
beforeEach(function() {
jest.resetModuleRegistry();
@@ -146,9 +150,10 @@ describe('ReactContextValidator', function() {
ReactTestUtils.renderIntoDocument();
expect(console.error.argsForCall.length).toBe(1);
- expect(console.error.argsForCall[0][0]).toBe(
- 'Warning: Failed Context Types: ' +
- 'Required context `foo` was not specified in `Component`.'
+ expect(normalizeCodeLocInfo(console.error.argsForCall[0][0])).toBe(
+ 'Warning: Failed context type: ' +
+ 'Required context `foo` was not specified in `Component`.\n' +
+ ' in Component (at **)'
);
var ComponentInFooStringContext = React.createClass({
@@ -193,11 +198,12 @@ describe('ReactContextValidator', function() {
ReactTestUtils.renderIntoDocument();
expect(console.error.argsForCall.length).toBe(2);
- expect(console.error.argsForCall[1][0]).toBe(
- 'Warning: Failed Context Types: ' +
+ expect(normalizeCodeLocInfo(console.error.argsForCall[1][0])).toBe(
+ 'Warning: Failed context type: ' +
'Invalid context `foo` of type `number` supplied ' +
- 'to `Component`, expected `string`.' +
- ' Check the render method of `ComponentInFooNumberContext`.'
+ 'to `Component`, expected `string`.\n' +
+ ' in Component (at **)\n' +
+ ' in ComponentInFooNumberContext (at **)'
);
});
@@ -221,18 +227,20 @@ describe('ReactContextValidator', function() {
ReactTestUtils.renderIntoDocument();
expect(console.error.argsForCall.length).toBe(1);
- expect(console.error.argsForCall[0][0]).toBe(
- 'Warning: Failed Context Types: ' +
- 'Required child context `foo` was not specified in `Component`.'
+ expect(normalizeCodeLocInfo(console.error.argsForCall[0][0])).toBe(
+ 'Warning: Failed childContext type: ' +
+ 'Required child context `foo` was not specified in `Component`.\n' +
+ ' in Component (at **)'
);
ReactTestUtils.renderIntoDocument();
expect(console.error.argsForCall.length).toBe(2);
- expect(console.error.argsForCall[1][0]).toBe(
- 'Warning: Failed Context Types: ' +
+ expect(normalizeCodeLocInfo(console.error.argsForCall[1][0])).toBe(
+ 'Warning: Failed childContext type: ' +
'Invalid child context `foo` of type `number` ' +
- 'supplied to `Component`, expected `string`.'
+ 'supplied to `Component`, expected `string`.\n' +
+ ' in Component (at **)'
);
ReactTestUtils.renderIntoDocument(
diff --git a/src/isomorphic/classic/element/ReactElementValidator.js b/src/isomorphic/classic/element/ReactElementValidator.js
index bd04c8f122f7b..2b7e9da3d56ad 100644
--- a/src/isomorphic/classic/element/ReactElementValidator.js
+++ b/src/isomorphic/classic/element/ReactElementValidator.js
@@ -21,12 +21,12 @@
var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');
var ReactElement = require('ReactElement');
-var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var ReactPropTypeLocations = require('ReactPropTypeLocations');
+var checkReactTypeSpec = require('checkReactTypeSpec');
+
var canDefineProperty = require('canDefineProperty');
var getIteratorFn = require('getIteratorFn');
-var invariant = require('invariant');
var warning = require('warning');
function getDeclarationErrorAddendum() {
@@ -46,8 +46,6 @@ function getDeclarationErrorAddendum() {
*/
var ownerHasKeyUseWarning = {};
-var loggedTypeFailures = {};
-
function getCurrentComponentErrorInfo(parentType) {
var info = getDeclarationErrorAddendum();
@@ -152,66 +150,6 @@ function validateChildKeys(node, parentType) {
}
}
-/**
- * Assert that the props are valid
- *
- * @param {object} element
- * @param {string} componentName Name of the component for error messages.
- * @param {object} propTypes Map of prop name to a ReactPropType
- * @param {string} location e.g. "prop", "context", "child context"
- * @private
- */
-function checkPropTypes(element, componentName, propTypes, location) {
- var props = element.props;
- for (var propName in propTypes) {
- if (propTypes.hasOwnProperty(propName)) {
- var error;
- // Prop type validation may throw. In case they do, we don't want to
- // 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.
- 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;
- }
- warning(
- !error || error instanceof Error,
- '%s: type specification of %s `%s` is invalid; the type checker ' +
- 'function must return `null` or an `Error` but returned a %s. ' +
- 'You may have forgotten to pass an argument to the type checker ' +
- 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
- 'shape all require an argument).',
- componentName || 'React class',
- ReactPropTypeLocationNames[location],
- propName,
- typeof error
- );
- if (error instanceof Error && !(error.message in loggedTypeFailures)) {
- // Only monitor this failure once because there tends to be a lot of the
- // same error.
- loggedTypeFailures[error.message] = true;
-
- warning(
- false,
- 'Failed propType: %s%s',
- error.message,
- ReactComponentTreeDevtool.getCurrentStackAddendum(element)
- );
- }
- }
- }
-}
-
/**
* Given an element, validate that its props follow the propTypes definition,
* provided by the type.
@@ -225,11 +163,13 @@ function validatePropTypes(element) {
}
var name = componentClass.displayName || componentClass.name;
if (componentClass.propTypes) {
- checkPropTypes(
- element,
- name,
+ checkReactTypeSpec(
componentClass.propTypes,
- ReactPropTypeLocations.prop
+ element.props,
+ ReactPropTypeLocations.prop,
+ name,
+ element,
+ null
);
}
if (typeof componentClass.getDefaultProps === 'function') {
diff --git a/src/isomorphic/classic/element/__tests__/ReactElementClone-test.js b/src/isomorphic/classic/element/__tests__/ReactElementClone-test.js
index b062870d17e14..322bcfb174e9f 100644
--- a/src/isomorphic/classic/element/__tests__/ReactElementClone-test.js
+++ b/src/isomorphic/classic/element/__tests__/ReactElementClone-test.js
@@ -269,7 +269,7 @@ describe('ReactElementClone', function() {
ReactTestUtils.renderIntoDocument(React.createElement(GrandParent));
expect(console.error.argsForCall.length).toBe(1);
expect(console.error.argsForCall[0][0]).toBe(
- 'Warning: Failed propType: ' +
+ 'Warning: Failed prop type: ' +
'Invalid prop `color` of type `number` supplied to `Component`, ' +
'expected `string`.\n' +
' in Component (created by GrandParent)\n' +
diff --git a/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js b/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js
index 5f640d5e96fd7..69872e3dd30d2 100644
--- a/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js
+++ b/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js
@@ -281,7 +281,7 @@ describe('ReactElementValidator', function() {
});
ReactTestUtils.renderIntoDocument(React.createElement(ParentComp));
expect(console.error.argsForCall[0][0]).toBe(
- 'Warning: Failed propType: ' +
+ 'Warning: Failed prop type: ' +
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
'expected `string`.\n' +
' in MyComp (created by ParentComp)\n' +
@@ -360,7 +360,7 @@ describe('ReactElementValidator', function() {
expect(console.error.calls.length).toBe(1);
expect(console.error.argsForCall[0][0]).toBe(
- 'Warning: Failed propType: ' +
+ 'Warning: Failed prop type: ' +
'Required prop `prop` was not specified in `Component`.\n' +
' in Component'
);
@@ -385,7 +385,7 @@ describe('ReactElementValidator', function() {
expect(console.error.calls.length).toBe(1);
expect(console.error.argsForCall[0][0]).toBe(
- 'Warning: Failed propType: ' +
+ 'Warning: Failed prop type: ' +
'Required prop `prop` was not specified in `Component`.\n' +
' in Component'
);
@@ -412,13 +412,13 @@ describe('ReactElementValidator', function() {
expect(console.error.calls.length).toBe(2);
expect(console.error.argsForCall[0][0]).toBe(
- 'Warning: Failed propType: ' +
+ 'Warning: Failed prop type: ' +
'Required prop `prop` was not specified in `Component`.\n' +
' in Component'
);
expect(console.error.argsForCall[1][0]).toBe(
- 'Warning: Failed propType: ' +
+ 'Warning: Failed prop type: ' +
'Invalid prop `prop` of type `number` supplied to ' +
'`Component`, expected `string`.\n' +
' in Component'
diff --git a/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js b/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
index cb778202c334c..b217438abe5c4 100644
--- a/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
+++ b/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
@@ -867,7 +867,7 @@ describe('ReactPropTypes', function() {
instance = ReactTestUtils.renderIntoDocument(instance);
expect(spy.argsForCall.length).toBe(1);
- expect(spy.argsForCall[0][1]).toBe('num');
+ expect(spy.argsForCall[0][1]).toBe('num');
});
it('should have received the validator\'s return value', function() {
@@ -894,7 +894,7 @@ describe('ReactPropTypes', function() {
expect(
console.error.argsForCall[0][0].replace(/\(at .+?:\d+\)/g, '(at **)')
).toBe(
- 'Warning: Failed propType: num must be 5!\n' +
+ 'Warning: Failed prop type: num must be 5!\n' +
' in Component (at **)'
);
});
diff --git a/src/isomorphic/classic/types/checkReactTypeSpec.js b/src/isomorphic/classic/types/checkReactTypeSpec.js
new file mode 100644
index 0000000000000..3b119b395fea2
--- /dev/null
+++ b/src/isomorphic/classic/types/checkReactTypeSpec.js
@@ -0,0 +1,93 @@
+/**
+ * Copyright 2013-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule checkReactTypeSpec
+ */
+
+'use strict';
+
+var ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');
+var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
+
+var invariant = require('invariant');
+var warning = require('warning');
+
+var loggedTypeFailures = {};
+
+/**
+ * Assert that the values match with the type specs.
+ * Error messages are memorized and will only be shown once.
+ *
+ * @param {object} typeSpecs Map of name to a ReactPropType
+ * @param {object} values Runtime values that need to be type-checked
+ * @param {string} location e.g. "prop", "context", "child context"
+ * @param {string} componentName Name of the component for error messages.
+ * @param {?object} element The React element that is being type-checked
+ * @param {?number} debugID The React component instance that is being type-checked
+ * @private
+ */
+function checkReactTypeSpec(typeSpecs, values, location, componentName, element, debugID) {
+ for (var typeSpecName in typeSpecs) {
+ if (typeSpecs.hasOwnProperty(typeSpecName)) {
+ var error;
+ // Prop type validation may throw. In case they do, we don't want to
+ // 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.
+ 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 typeSpecs[typeSpecName] === 'function',
+ '%s: %s type `%s` is invalid; it must be a function, usually from ' +
+ 'React.PropTypes.',
+ componentName || 'React class',
+ ReactPropTypeLocationNames[location],
+ typeSpecName
+ );
+ error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location);
+ } catch (ex) {
+ error = ex;
+ }
+ warning(
+ !error || error instanceof Error,
+ '%s: type specification of %s `%s` is invalid; the type checker ' +
+ 'function must return `null` or an `Error` but returned a %s. ' +
+ 'You may have forgotten to pass an argument to the type checker ' +
+ 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
+ 'shape all require an argument).',
+ componentName || 'React class',
+ ReactPropTypeLocationNames[location],
+ typeSpecName,
+ typeof error
+ );
+ if (error instanceof Error && !(error.message in loggedTypeFailures)) {
+ // Only monitor this failure once because there tends to be a lot of the
+ // same error.
+ loggedTypeFailures[error.message] = true;
+
+ var componentStackInfo = '';
+
+ if (debugID !== null) {
+ componentStackInfo = ReactComponentTreeDevtool.getStackAddendumByID(debugID);
+ } else if (element !== null) {
+ componentStackInfo = ReactComponentTreeDevtool.getCurrentStackAddendum(element);
+ }
+
+ warning(
+ false,
+ 'Failed %s type: %s%s',
+ location,
+ error.message,
+ componentStackInfo
+ );
+ }
+ }
+ }
+}
+
+module.exports = checkReactTypeSpec;
diff --git a/src/isomorphic/modern/element/__tests__/ReactJSXElementValidator-test.js b/src/isomorphic/modern/element/__tests__/ReactJSXElementValidator-test.js
index 427d2340bb305..c6429dfc24097 100644
--- a/src/isomorphic/modern/element/__tests__/ReactJSXElementValidator-test.js
+++ b/src/isomorphic/modern/element/__tests__/ReactJSXElementValidator-test.js
@@ -198,7 +198,7 @@ describe('ReactJSXElementValidator', function() {
expect(
console.error.argsForCall[0][0].replace(/\(at .+?:\d+\)/g, '(at **)')
).toBe(
- 'Warning: Failed propType: ' +
+ 'Warning: Failed prop type: ' +
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
'expected `string`.\n' +
' in MyComp (at **)\n' +
@@ -249,7 +249,7 @@ describe('ReactJSXElementValidator', function() {
expect(
console.error.argsForCall[0][0].replace(/\(at .+?:\d+\)/g, '(at **)')
).toBe(
- 'Warning: Failed propType: ' +
+ 'Warning: Failed prop type: ' +
'Required prop `prop` was not specified in `RequiredPropComponent`.\n' +
' in RequiredPropComponent (at **)'
);
@@ -264,7 +264,7 @@ describe('ReactJSXElementValidator', function() {
expect(
console.error.argsForCall[0][0].replace(/\(at .+?:\d+\)/g, '(at **)')
).toBe(
- 'Warning: Failed propType: ' +
+ 'Warning: Failed prop type: ' +
'Required prop `prop` was not specified in `RequiredPropComponent`.\n' +
' in RequiredPropComponent (at **)'
);
@@ -280,7 +280,7 @@ describe('ReactJSXElementValidator', function() {
expect(
console.error.argsForCall[0][0].replace(/\(at .+?:\d+\)/g, '(at **)')
).toBe(
- 'Warning: Failed propType: ' +
+ 'Warning: Failed prop type: ' +
'Required prop `prop` was not specified in `RequiredPropComponent`.\n' +
' in RequiredPropComponent (at **)'
);
@@ -288,7 +288,7 @@ describe('ReactJSXElementValidator', function() {
expect(
console.error.argsForCall[1][0].replace(/\(at .+?:\d+\)/g, '(at **)')
).toBe(
- 'Warning: Failed propType: ' +
+ 'Warning: Failed prop type: ' +
'Invalid prop `prop` of type `number` supplied to ' +
'`RequiredPropComponent`, expected `string`.\n' +
' in RequiredPropComponent (at **)'
diff --git a/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js b/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
index b9ca072e556d4..7a02c1904a7f6 100644
--- a/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
+++ b/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
@@ -19,26 +19,16 @@ var ReactInstanceMap = require('ReactInstanceMap');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactNodeTypes = require('ReactNodeTypes');
var ReactPropTypeLocations = require('ReactPropTypeLocations');
-var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var ReactReconciler = require('ReactReconciler');
var ReactUpdateQueue = require('ReactUpdateQueue');
+var checkReactTypeSpec = require('checkReactTypeSpec');
+
var emptyObject = require('emptyObject');
var invariant = require('invariant');
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
var warning = require('warning');
-function getDeclarationErrorAddendum(component) {
- var owner = component._currentElement._owner || null;
- if (owner) {
- var name = owner.getName();
- if (name) {
- return ' Check the render method of `' + name + '`.';
- }
- }
- return '';
-}
-
function StatelessComponent(Component) {
}
StatelessComponent.prototype.render = function() {
@@ -669,45 +659,20 @@ var ReactCompositeComponentMixin = {
/**
* Assert that the context types are valid
*
- * @param {object} propTypes Map of context field to a ReactPropType
- * @param {object} props
+ * @param {object} typeSpecs Map of context field to a ReactPropType
+ * @param {object} values Runtime values that need to be type-checked
* @param {string} location e.g. "prop", "context", "child context"
* @private
*/
- _checkContextTypes: function(propTypes, props, location) {
- var componentName = this.getName();
- for (var propName in propTypes) {
- if (propTypes.hasOwnProperty(propName)) {
- var error;
- 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) {
- // We may want to extend this logic for similar errors in
- // top-level render calls, so I'm abstracting it away into
- // a function to minimize refactoring in the future
- var addendum = getDeclarationErrorAddendum(this);
- warning(
- false,
- 'Failed Context Types: %s%s',
- error.message,
- addendum
- );
- }
- }
- }
+ _checkContextTypes: function(typeSpecs, values, location) {
+ checkReactTypeSpec(
+ typeSpecs,
+ values,
+ location,
+ this.getName(),
+ null,
+ this._debugID
+ );
},
receiveComponent: function(nextElement, transaction, nextContext) {
diff --git a/src/renderers/shared/stack/reconciler/__tests__/ReactStatelessComponent-test.js b/src/renderers/shared/stack/reconciler/__tests__/ReactStatelessComponent-test.js
index c5b2744cebddc..56eceb67f2425 100644
--- a/src/renderers/shared/stack/reconciler/__tests__/ReactStatelessComponent-test.js
+++ b/src/renderers/shared/stack/reconciler/__tests__/ReactStatelessComponent-test.js
@@ -178,7 +178,7 @@ describe('ReactStatelessComponent', function() {
expect(
console.error.argsForCall[0][0].replace(/\(at .+?:\d+\)/g, '(at **)')
).toBe(
- 'Warning: Failed propType: Invalid prop `test` of type `number` ' +
+ 'Warning: Failed prop type: Invalid prop `test` of type `number` ' +
'supplied to `Child`, expected `string`.\n' +
' in Child (at **)'
);