From 642323e5a8ad814e372b67bd76ded2d6d5bd383d Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Wed, 17 Jun 2015 00:16:22 -0700 Subject: [PATCH] Improve error message when mounting non-string/function elements --- .../__tests__/ReactElementValidator-test.js | 11 ++++---- .../__tests__/ReactComponent-test.js | 28 +++++++++++++++++++ .../reconciler/instantiateReactComponent.js | 25 ++++++++++++----- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js b/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js index 8b9d5822efccd..fe3d1159cc13e 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 77179a75ae060..9b4a997f16bca 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 9f470c04c1ae8..286cf9bfb4a2c 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 &&