From 5c68d1d54ec0b306818dbedfd0f5d9e3f1364138 Mon Sep 17 00:00:00 2001 From: HeroProtagonist Date: Fri, 2 Feb 2018 12:13:13 -0500 Subject: [PATCH 1/4] Match error message to one in `ReactFiber.js` --- .../ReactDOMServerIntegrationElements-test.js | 15 ++++++++++++ .../src/server/ReactPartialRenderer.js | 23 ++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js index b03f9ed36425b..84bdb9018b7cf 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js @@ -870,6 +870,21 @@ describe('ReactDOMServerIntegration', () => { 'an array instead.' : ''), ); + + itThrowsWhenRendering( + 'badly-typed elements', + async render => { + spyOnDev(console, 'error'); + const EmptyComponent = {}; + await render(); + }, + 'Element type is invalid: expected a string (for built-in components) or a class/function ' + + '(for composite components) but got: object.' + + (__DEV__ + ? " You likely forgot to export your component from the file it's defined in, " + + 'or you might have mixed up default and named imports.' + : ''), + ); }); }); }); diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index 656f20fc7144a..2e7047f397d28 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -889,12 +889,33 @@ class ReactDOMServerRenderer { break; } } + + let info = ''; + if (__DEV__) { + const owner = elementType._owner; + if ( + elementType === undefined || + (typeof elementType === 'object' && + elementType !== null && + Object.keys(elementType).length === 0) + ) { + info += + ' You likely forgot to export your component from the file ' + + "it's defined in, or you might have mixed up default and " + + 'named imports.'; + } + const ownerName = owner ? getComponentName(owner) : null; + if (ownerName) { + info += '\n\nCheck the render method of `' + ownerName + '`.'; + } + } invariant( false, 'Element type is invalid: expected a string (for built-in ' + 'components) or a class/function (for composite components) ' + - 'but got: %s.', + 'but got: %s.%s', elementType == null ? elementType : typeof elementType, + info, ); } } From a398bc2bf4333c70a4347ef649825575382aa9ef Mon Sep 17 00:00:00 2001 From: HeroProtagonist Date: Fri, 2 Feb 2018 19:19:00 -0500 Subject: [PATCH 2/4] Add undefined/null guard and tests --- .../ReactDOMServerIntegrationElements-test.js | 26 +++++++++++++++++++ .../src/server/ReactPartialRenderer.js | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js index 84bdb9018b7cf..e55af43f2b901 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js @@ -885,6 +885,32 @@ describe('ReactDOMServerIntegration', () => { 'or you might have mixed up default and named imports.' : ''), ); + + itThrowsWhenRendering( + 'null', + async render => { + spyOnDev(console, 'error'); + const NullComponent = null; + await render(); + }, + 'Element type is invalid: expected a string (for built-in components) or a class/function ' + + '(for composite components) but got: null.', + ); + + itThrowsWhenRendering( + 'undefined', + async render => { + spyOnDev(console, 'error'); + const UndefinedComponent = undefined; + await render(); + }, + 'Element type is invalid: expected a string (for built-in components) or a class/function ' + + '(for composite components) but got: undefined.' + + (__DEV__ + ? " You likely forgot to export your component from the file it's defined in, " + + 'or you might have mixed up default and named imports.' + : ''), + ); }); }); }); diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index 2e7047f397d28..e888d07281594 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -892,7 +892,7 @@ class ReactDOMServerRenderer { let info = ''; if (__DEV__) { - const owner = elementType._owner; + const owner = elementType && elementType._owner; if ( elementType === undefined || (typeof elementType === 'object' && From 6d69bdbcd10bb8baab22e887ad3e935a3b7d87c8 Mon Sep 17 00:00:00 2001 From: HeroProtagonist Date: Sun, 4 Feb 2018 20:15:53 -0500 Subject: [PATCH 3/4] Update tests and element check --- .../ReactDOMServerIntegrationElements-test.js | 34 ++++++++++++++++--- .../src/server/ReactPartialRenderer.js | 2 +- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js index e55af43f2b901..74ebb784a7240 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js @@ -870,11 +870,39 @@ describe('ReactDOMServerIntegration', () => { 'an array instead.' : ''), ); + }); + + describe('badly-typed elements', function() { + beforeEach(() => { + if (__DEV__) { + spyOnDev(console, 'error'); + const originalCreateElement = React.createElement; + spyOnDev(React, 'createElement').and.callFake(el => { + const reactElement = originalCreateElement(el); + + const isNull = el === null; + const receivedType = isNull ? 'null' : typeof el; + + expect(console.error.calls.count()).toBe(1); + expect(console.error.calls.argsFor(0)[0]).toContain( + 'Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a ' + + `class/function (for composite components) but got: ${ + receivedType + }.` + + (!isNull + ? " You likely forgot to export your component from the file it's defined in, " + + 'or you might have mixed up default and named imports.' + : ''), + ); + + return reactElement; + }); + } + }); itThrowsWhenRendering( - 'badly-typed elements', + 'object', async render => { - spyOnDev(console, 'error'); const EmptyComponent = {}; await render(); }, @@ -889,7 +917,6 @@ describe('ReactDOMServerIntegration', () => { itThrowsWhenRendering( 'null', async render => { - spyOnDev(console, 'error'); const NullComponent = null; await render(); }, @@ -900,7 +927,6 @@ describe('ReactDOMServerIntegration', () => { itThrowsWhenRendering( 'undefined', async render => { - spyOnDev(console, 'error'); const UndefinedComponent = undefined; await render(); }, diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index e888d07281594..e774aeaace83d 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -892,7 +892,7 @@ class ReactDOMServerRenderer { let info = ''; if (__DEV__) { - const owner = elementType && elementType._owner; + const owner = nextElement._owner; if ( elementType === undefined || (typeof elementType === 'object' && From f1e2542b0cd546bfc6d8de7de9fd6273d215ef7c Mon Sep 17 00:00:00 2001 From: HeroProtagonist Date: Mon, 5 Feb 2018 11:56:56 -0500 Subject: [PATCH 4/4] Remove beforeEach block --- .../ReactDOMServerIntegrationElements-test.js | 67 +++++++++---------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js index 74ebb784a7240..f50253904d698 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js @@ -873,38 +873,20 @@ describe('ReactDOMServerIntegration', () => { }); describe('badly-typed elements', function() { - beforeEach(() => { - if (__DEV__) { - spyOnDev(console, 'error'); - const originalCreateElement = React.createElement; - spyOnDev(React, 'createElement').and.callFake(el => { - const reactElement = originalCreateElement(el); - - const isNull = el === null; - const receivedType = isNull ? 'null' : typeof el; - - expect(console.error.calls.count()).toBe(1); - expect(console.error.calls.argsFor(0)[0]).toContain( - 'Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a ' + - `class/function (for composite components) but got: ${ - receivedType - }.` + - (!isNull - ? " You likely forgot to export your component from the file it's defined in, " + - 'or you might have mixed up default and named imports.' - : ''), - ); - - return reactElement; - }); - } - }); - itThrowsWhenRendering( 'object', async render => { - const EmptyComponent = {}; - await render(); + let EmptyComponent = {}; + expect(() => { + EmptyComponent = ; + }).toWarnDev( + 'Warning: React.createElement: type is invalid -- expected a string ' + + '(for built-in components) or a class/function (for composite ' + + 'components) but got: object. You likely forgot to export your ' + + "component from the file it's defined in, or you might have mixed up " + + 'default and named imports.', + ); + await render(EmptyComponent); }, 'Element type is invalid: expected a string (for built-in components) or a class/function ' + '(for composite components) but got: object.' + @@ -917,18 +899,35 @@ describe('ReactDOMServerIntegration', () => { itThrowsWhenRendering( 'null', async render => { - const NullComponent = null; - await render(); + let NullComponent = null; + expect(() => { + NullComponent = ; + }).toWarnDev( + 'Warning: React.createElement: type is invalid -- expected a string ' + + '(for built-in components) or a class/function (for composite ' + + 'components) but got: null.', + ); + await render(NullComponent); }, 'Element type is invalid: expected a string (for built-in components) or a class/function ' + - '(for composite components) but got: null.', + '(for composite components) but got: null', ); itThrowsWhenRendering( 'undefined', async render => { - const UndefinedComponent = undefined; - await render(); + let UndefinedComponent = undefined; + expect(() => { + UndefinedComponent = ; + }).toWarnDev( + 'Warning: React.createElement: type is invalid -- expected a string ' + + '(for built-in components) or a class/function (for composite ' + + 'components) but got: undefined. You likely forgot to export your ' + + "component from the file it's defined in, or you might have mixed up " + + 'default and named imports.', + ); + + await render(UndefinedComponent); }, 'Element type is invalid: expected a string (for built-in components) or a class/function ' + '(for composite components) but got: undefined.' +