refactor and simplify app dynamic components (#59658)
This commit is contained in:
parent
7faaeeb9b0
commit
d3205561d2
3 changed files with 61 additions and 56 deletions
|
@ -1,34 +1,18 @@
|
|||
import React from 'react'
|
||||
import Loadable from './lazy-dynamic/loadable'
|
||||
|
||||
type ComponentModule<P = {}> = { default: React.ComponentType<P> }
|
||||
import type {
|
||||
LoadableGeneratedOptions,
|
||||
DynamicOptionsLoadingProps,
|
||||
Loader,
|
||||
LoaderComponent,
|
||||
} from './lazy-dynamic/types'
|
||||
|
||||
export declare type LoaderComponent<P = {}> = Promise<
|
||||
React.ComponentType<P> | ComponentModule<P>
|
||||
>
|
||||
|
||||
export declare type Loader<P = {}> = () => LoaderComponent<P>
|
||||
|
||||
export type LoaderMap = { [module: string]: () => Loader<any> }
|
||||
|
||||
export type LoadableGeneratedOptions = {
|
||||
webpack?(): any
|
||||
modules?(): LoaderMap
|
||||
}
|
||||
|
||||
export type DynamicOptionsLoadingProps = {
|
||||
error?: Error | null
|
||||
isLoading?: boolean
|
||||
pastDelay?: boolean
|
||||
retry?: () => void
|
||||
timedOut?: boolean
|
||||
}
|
||||
|
||||
// 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<P>(mod: React.ComponentType<P> | ComponentModule<P>) {
|
||||
return { default: (mod as ComponentModule<P>)?.default || mod }
|
||||
export {
|
||||
type LoadableGeneratedOptions,
|
||||
type DynamicOptionsLoadingProps,
|
||||
type Loader,
|
||||
type LoaderComponent,
|
||||
}
|
||||
|
||||
export type DynamicOptions<P = {}> = LoadableGeneratedOptions & {
|
||||
|
@ -50,8 +34,6 @@ export default function dynamic<P = {}>(
|
|||
dynamicOptions: DynamicOptions<P> | Loader<P>,
|
||||
options?: DynamicOptions<P>
|
||||
): React.ComponentType<P> {
|
||||
const loadableFn: LoadableFn<P> = Loadable
|
||||
|
||||
const loadableOptions: LoadableOptions<P> = {
|
||||
// A loading component is not required, so we default it
|
||||
loading: ({ error, isLoading, pastDelay }) => {
|
||||
|
@ -78,13 +60,5 @@ export default function dynamic<P = {}>(
|
|||
loadableOptions.loader = dynamicOptions
|
||||
}
|
||||
|
||||
Object.assign(loadableOptions, options)
|
||||
|
||||
const loaderFn = loadableOptions.loader as () => LoaderComponent<P>
|
||||
const loader = () =>
|
||||
loaderFn != null
|
||||
? loaderFn().then(convertModule)
|
||||
: Promise.resolve(convertModule(() => null))
|
||||
|
||||
return loadableFn({ ...loadableOptions, loader: loader as Loader<P> })
|
||||
return Loadable({ ...loadableOptions, ...options })
|
||||
}
|
||||
|
|
|
@ -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<P>(mod: React.ComponentType<P> | ComponentModule<P>) {
|
||||
return { default: (mod as ComponentModule<P>)?.default || mod }
|
||||
}
|
||||
|
||||
function Loadable(options: any) {
|
||||
const opts = Object.assign(
|
||||
{
|
||||
const opts = {
|
||||
loader: null,
|
||||
loading: null,
|
||||
ssr: true,
|
||||
},
|
||||
options
|
||||
)
|
||||
...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 ? (
|
||||
<Loading isLoading={true} pastDelay={true} error={null} />
|
||||
)
|
||||
|
||||
const Wrap = opts.ssr ? React.Fragment : NoSSR
|
||||
const Lazy = opts.lazy
|
||||
) : null
|
||||
|
||||
return (
|
||||
<React.Suspense fallback={fallbackElement}>
|
||||
<Suspense fallback={fallbackElement}>
|
||||
<Wrap>
|
||||
<Lazy {...props} />
|
||||
</Wrap>
|
||||
</React.Suspense>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
22
packages/next/src/shared/lib/lazy-dynamic/types.ts
Normal file
22
packages/next/src/shared/lib/lazy-dynamic/types.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
export type ComponentModule<P = {}> = { default: React.ComponentType<P> }
|
||||
|
||||
export declare type LoaderComponent<P = {}> = Promise<
|
||||
React.ComponentType<P> | ComponentModule<P>
|
||||
>
|
||||
|
||||
export declare type Loader<P = {}> = () => LoaderComponent<P>
|
||||
|
||||
export type LoaderMap = { [module: string]: () => Loader<any> }
|
||||
|
||||
export type LoadableGeneratedOptions = {
|
||||
webpack?(): any
|
||||
modules?(): LoaderMap
|
||||
}
|
||||
|
||||
export type DynamicOptionsLoadingProps = {
|
||||
error?: Error | null
|
||||
isLoading?: boolean
|
||||
pastDelay?: boolean
|
||||
retry?: () => void
|
||||
timedOut?: boolean
|
||||
}
|
Loading…
Reference in a new issue