From 40d016a5d66f9aa37cba5368b659eafd2e14254c Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 21 Nov 2018 18:27:18 +0000 Subject: [PATCH] Improve error message for nested lazy() and add tests --- .../src/ReactFiberBeginWork.js | 14 +++++++- .../src/__tests__/ReactLazy-test.internal.js | 34 ++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index c6f6e3c1fd..a6de9c5009 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -54,6 +54,7 @@ import invariant from 'shared/invariant'; import shallowEqual from 'shared/shallowEqual'; import getComponentName from 'shared/getComponentName'; import ReactStrictModeWarnings from './ReactStrictModeWarnings'; +import {REACT_LAZY_TYPE} from 'shared/ReactSymbols'; import warning from 'shared/warning'; import warningWithoutStack from 'shared/warningWithoutStack'; import { @@ -838,14 +839,25 @@ function mountLazyComponent( break; } default: { + let hint = ''; + if (__DEV__) { + if ( + Component !== null && + typeof Component === 'object' && + Component.$$typeof === REACT_LAZY_TYPE + ) { + hint = ' Did you wrap a component in React.lazy() more than once?'; + } + } // This message intentionally doesn't mention ForwardRef or MemoComponent // because the fact that it's a separate type of work is an // implementation detail. invariant( false, 'Element type is invalid. Received a promise that resolves to: %s. ' + - 'Lazy element type must resolve to a class or function.', + 'Lazy element type must resolve to a class or function.%s', Component, + hint, ); } } diff --git a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js index 575d7b4db7..0ea276d0fd 100644 --- a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js @@ -512,6 +512,35 @@ describe('ReactLazy', () => { expect(root).toMatchRenderedOutput('Friends Bye'); }); + it('throws with a useful error when wrapping invalid type with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(42)); + + const root = ReactTestRenderer.create( + }> + + , + { + unstable_isConcurrent: true, + }, + ); + + expect(root).toFlushAndYield(['Loading...']); + expect(root).toMatchRenderedOutput(null); + + await Promise.resolve(); + root.update( + }> + + , + ); + expect(() => { + root.unstable_flushAll(); + }).toThrow( + 'Element type is invalid. Received a promise that resolves to: 42. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + it('throws with a useful error when wrapping lazy() multiple times', async () => { const Lazy1 = lazy(() => fakeImport(Text)); const Lazy2 = lazy(() => fakeImport(Lazy1)); @@ -538,7 +567,10 @@ describe('ReactLazy', () => { root.unstable_flushAll(); }).toThrow( 'Element type is invalid. Received a promise that resolves to: [object Object]. ' + - 'Lazy element type must resolve to a class or function.', + 'Lazy element type must resolve to a class or function.' + + (__DEV__ + ? ' Did you wrap a component in React.lazy() more than once?' + : ''), ); });