diff --git a/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js b/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js
index 8b9d5822ef..fe3d1159cc 100644
--- a/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js
+++ b/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js
@@ -327,17 +327,18 @@ describe('ReactElementValidator', function() {
});
expect(function() {
ReactTestUtils.renderIntoDocument(React.createElement(ParentComp));
- }).toThrow();
- expect(console.error.calls.length).toBe(2);
+ }).toThrow(
+ 'Invariant Violation: Element type is invalid: expected a string (for ' +
+ 'built-in components) or a class/function (for composite components) ' +
+ 'but got: null. Check the render method of `ParentComp`.'
+ );
+ expect(console.error.calls.length).toBe(1);
expect(console.error.calls[0].args[0]).toBe(
'Warning: React.createElement: type should not be null, undefined, ' +
'boolean, or number. It should be a string (for DOM elements) or a ' +
'ReactClass (for composite components). Check the render method of ' +
'`ParentComp`.'
);
- expect(console.error.calls[1].args[0]).toBe(
- 'Warning: Only functions or strings can be mounted as React components.'
- );
});
it('should check default prop values', function() {
diff --git a/src/renderers/shared/reconciler/__tests__/ReactComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactComponent-test.js
index 77179a75ae..9b4a997f16 100644
--- a/src/renderers/shared/reconciler/__tests__/ReactComponent-test.js
+++ b/src/renderers/shared/reconciler/__tests__/ReactComponent-test.js
@@ -279,4 +279,32 @@ describe('ReactComponent', function() {
);
});
+ it('throws usefully when rendering badly-typed elements', function() {
+ spyOn(console, 'error');
+
+ var X = undefined;
+ expect(() => ReactTestUtils.renderIntoDocument()).toThrow(
+ 'Invariant Violation: Element type is invalid: expected a string (for ' +
+ 'built-in components) or a class/function (for composite components) ' +
+ 'but got: undefined.'
+ );
+
+ var Y = null;
+ expect(() => ReactTestUtils.renderIntoDocument()).toThrow(
+ 'Invariant Violation: Element type is invalid: expected a string (for ' +
+ 'built-in components) or a class/function (for composite components) ' +
+ 'but got: null.'
+ );
+
+ var Z = {};
+ expect(() => ReactTestUtils.renderIntoDocument()).toThrow(
+ 'Invariant Violation: Element type is invalid: expected a string (for ' +
+ 'built-in components) or a class/function (for composite components) ' +
+ 'but got: object.'
+ );
+
+ // One warning for each element creation
+ expect(console.error.calls.length).toBe(3);
+ });
+
});
diff --git a/src/renderers/shared/reconciler/instantiateReactComponent.js b/src/renderers/shared/reconciler/instantiateReactComponent.js
index 9f470c04c1..286cf9bfb4 100644
--- a/src/renderers/shared/reconciler/instantiateReactComponent.js
+++ b/src/renderers/shared/reconciler/instantiateReactComponent.js
@@ -30,6 +30,16 @@ assign(
}
);
+function getDeclarationErrorAddendum(owner) {
+ if (owner) {
+ var name = owner.getName();
+ if (name) {
+ return ' Check the render method of `' + name + '`.';
+ }
+ }
+ return '';
+}
+
/**
* Check if the type reference is a known internal type. I.e. not a user
* provided composite type.
@@ -63,13 +73,14 @@ function instantiateReactComponent(node, parentCompositeType) {
if (typeof node === 'object') {
var element = node;
- if (__DEV__) {
- warning(
- element && (typeof element.type === 'function' ||
- typeof element.type === 'string'),
- 'Only functions or strings can be mounted as React components.'
- );
- }
+ invariant(
+ element && (typeof element.type === 'function' ||
+ typeof element.type === 'string'),
+ 'Element type is invalid: expected a string (for built-in components) ' +
+ 'or a class/function (for composite components) but got: %s.%s',
+ element.type == null ? element.type : typeof element.type,
+ getDeclarationErrorAddendum(element._owner)
+ );
// Special case string values
if (parentCompositeType === element.type &&