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?'
+ : ''),
);
});