diff --git a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js index 5472c9c89fb6b..e8c2153cab94a 100644 --- a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js @@ -791,7 +791,7 @@ describe('ReactLazy', () => { ); }); - it('throws with a useful error when wrapping fragment with lazy()', async () => { + it('throws with a useful error when wrapping Fragment with lazy()', async () => { const BadLazy = lazy(() => fakeImport(React.Fragment)); const root = ReactTestRenderer.create( @@ -817,6 +817,280 @@ describe('ReactLazy', () => { ); }); + // @gate !fb + it('throws with a useful error when wrapping createPortal with lazy()', async () => { + const ReactDOM = require('react-dom'); + const container = document.createElement('div'); + const portal = ReactDOM.createPortal(
, container); + const BadLazy = lazy(() => fakeImport(portal)); + + const root = ReactTestRenderer.create( + }> + + , + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(portal); + root.update( + }> + + , + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: Portal. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + it('throws with a useful error when wrapping Profiler with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.Profiler)); + + const root = ReactTestRenderer.create( + }> + + , + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.Profiler); + root.update( + }> + + , + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: Profiler. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + it('throws with a useful error when wrapping StrictMode with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.StrictMode)); + + const root = ReactTestRenderer.create( + }> + + , + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.StrictMode); + root.update( + }> + + , + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: StrictMode. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + it('throws with a useful error when wrapping Suspense with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.Suspense)); + + const root = ReactTestRenderer.create( + }> + + , + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.Suspense); + root.update( + }> + + , + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: Suspense. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + it('throws with a useful error when wrapping Context with lazy()', async () => { + const Context = React.createContext(null); + const BadLazy = lazy(() => fakeImport(Context)); + + const root = ReactTestRenderer.create( + }> + + , + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(Context); + root.update( + }> + + , + ); + await waitForThrow( + gate('enableRenderableContext') + ? 'Element type is invalid. Received a promise that resolves to: Context.Provider. ' + + 'Lazy element type must resolve to a class or function.' + : 'Element type is invalid. Received a promise that resolves to: Context.Consumer. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + // @gate enableRenderableContext + it('throws with a useful error when wrapping Context.Consumer with lazy()', async () => { + const Context = React.createContext(null); + const BadLazy = lazy(() => fakeImport(Context.Consumer)); + + const root = ReactTestRenderer.create( + }> + + , + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(Context.Consumer); + root.update( + }> + + , + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: Context.Consumer. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + // @gate enableSuspenseList + it('throws with a useful error when wrapping SuspenseList with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.unstable_SuspenseList)); + + const root = ReactTestRenderer.create( + }> + + , + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.unstable_SuspenseList); + root.update( + }> + + , + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: SuspenseList. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + // @gate enableViewTransition + it('throws with a useful error when wrapping ViewTransition with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.unstable_ViewTransition)); + + const root = ReactTestRenderer.create( + }> + + , + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.unstable_ViewTransition); + root.update( + }> + + , + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: ViewTransition. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + // @gate enableActivity + it('throws with a useful error when wrapping Activity with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.unstable_Activity)); + + const root = ReactTestRenderer.create( + }> + + , + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.unstable_Activity); + root.update( + }> + + , + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: Activity. ' + + 'Lazy element type must resolve to a class or function.', + ); + }); + + // @gate enableTransitionTracing + it('throws with a useful error when wrapping TracingMarker with lazy()', async () => { + const BadLazy = lazy(() => fakeImport(React.unstable_TracingMarker)); + + const root = ReactTestRenderer.create( + }> + + , + { + unstable_isConcurrent: true, + }, + ); + + await waitForAll(['Loading...']); + + await resolveFakeImport(React.unstable_TracingMarker); + root.update( + }> + + , + ); + await waitForThrow( + 'Element type is invalid. Received a promise that resolves to: TracingMarker. ' + + '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)); diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index 002652080ca3d..da5cba301edc1 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -74,8 +74,6 @@ export default function getComponentNameFromType(type: mixed): string | null { switch (type) { case REACT_FRAGMENT_TYPE: return 'Fragment'; - case REACT_PORTAL_TYPE: - return 'Portal'; case REACT_PROFILER_TYPE: return 'Profiler'; case REACT_STRICT_MODE_TYPE: @@ -106,6 +104,8 @@ export default function getComponentNameFromType(type: mixed): string | null { } } switch (type.$$typeof) { + case REACT_PORTAL_TYPE: + return 'Portal'; case REACT_PROVIDER_TYPE: if (enableRenderableContext) { return null;