diff --git a/packages/next/src/shared/lib/app-dynamic.tsx b/packages/next/src/shared/lib/app-dynamic.tsx index 6884ad9d35c36..9efbc8aed56b6 100644 --- a/packages/next/src/shared/lib/app-dynamic.tsx +++ b/packages/next/src/shared/lib/app-dynamic.tsx @@ -1,34 +1,18 @@ import React from 'react' import Loadable from './lazy-dynamic/loadable' -type ComponentModule
= { default: React.ComponentType
} - -export declare type LoaderComponent
= Promise< - React.ComponentType
| ComponentModule
-> - -export declare type Loader
= () => LoaderComponent
-
-export type LoaderMap = { [module: string]: () => Loader (mod: React.ComponentType | ComponentModule ) {
- return { default: (mod as ComponentModule )?.default || mod }
+import type {
+ LoadableGeneratedOptions,
+ DynamicOptionsLoadingProps,
+ Loader,
+ LoaderComponent,
+} from './lazy-dynamic/types'
+
+export {
+ type LoadableGeneratedOptions,
+ type DynamicOptionsLoadingProps,
+ type Loader,
+ type LoaderComponent,
}
export type DynamicOptions = LoadableGeneratedOptions & {
@@ -50,8 +34,6 @@ export default function dynamic (
dynamicOptions: DynamicOptions | Loader ,
options?: DynamicOptions
): React.ComponentType {
- const loadableFn: LoadableFn = Loadable
-
const loadableOptions: LoadableOptions = {
// A loading component is not required, so we default it
loading: ({ error, isLoading, pastDelay }) => {
@@ -78,13 +60,5 @@ export default function dynamic (
loadableOptions.loader = dynamicOptions
}
- Object.assign(loadableOptions, options)
-
- const loaderFn = loadableOptions.loader as () => LoaderComponent
- const loader = () =>
- loaderFn != null
- ? loaderFn().then(convertModule)
- : Promise.resolve(convertModule(() => null))
-
- return loadableFn({ ...loadableOptions, loader: loader as Loader })
+ return Loadable({ ...loadableOptions, ...options })
}
diff --git a/packages/next/src/shared/lib/lazy-dynamic/loadable.tsx b/packages/next/src/shared/lib/lazy-dynamic/loadable.tsx
index 69b41545e6a96..d5dc700d8ee53 100644
--- a/packages/next/src/shared/lib/lazy-dynamic/loadable.tsx
+++ b/packages/next/src/shared/lib/lazy-dynamic/loadable.tsx
@@ -1,33 +1,42 @@
-import React from 'react'
+import { Suspense, lazy, Fragment } from 'react'
import { NoSSR } from './dynamic-no-ssr'
+import type { ComponentModule } from './types'
+
+// Normalize loader to return the module as form { default: Component } for `React.lazy`.
+// Also for backward compatible since next/dynamic allows to resolve a component directly with loader
+// Client component reference proxy need to be converted to a module.
+function convertModule (mod: React.ComponentType | ComponentModule ) {
+ return { default: (mod as ComponentModule )?.default || mod }
+}
function Loadable(options: any) {
- const opts = Object.assign(
- {
- loader: null,
- loading: null,
- ssr: true,
- },
- options
- )
+ const opts = {
+ loader: null,
+ loading: null,
+ ssr: true,
+ ...options,
+ }
- opts.lazy = React.lazy(opts.loader)
+ const loader = () =>
+ opts.loader != null
+ ? opts.loader().then(convertModule)
+ : Promise.resolve(convertModule(() => null))
+
+ const Lazy = lazy(loader)
+ const Loading = opts.loading
+ const Wrap = opts.ssr ? Fragment : NoSSR
function LoadableComponent(props: any) {
- const Loading = opts.loading
- const fallbackElement = (
+ const fallbackElement = Loading ? (
= { default: React.ComponentType }
+
+export declare type LoaderComponent = Promise<
+ React.ComponentType | ComponentModule
+>
+
+export declare type Loader = () => LoaderComponent
+
+export type LoaderMap = { [module: string]: () => Loader