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 React from 'react'
|
||||||
import Loadable from './lazy-dynamic/loadable'
|
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<
|
export {
|
||||||
React.ComponentType<P> | ComponentModule<P>
|
type LoadableGeneratedOptions,
|
||||||
>
|
type DynamicOptionsLoadingProps,
|
||||||
|
type Loader,
|
||||||
export declare type Loader<P = {}> = () => LoaderComponent<P>
|
type LoaderComponent,
|
||||||
|
|
||||||
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 DynamicOptions<P = {}> = LoadableGeneratedOptions & {
|
export type DynamicOptions<P = {}> = LoadableGeneratedOptions & {
|
||||||
|
@ -50,8 +34,6 @@ export default function dynamic<P = {}>(
|
||||||
dynamicOptions: DynamicOptions<P> | Loader<P>,
|
dynamicOptions: DynamicOptions<P> | Loader<P>,
|
||||||
options?: DynamicOptions<P>
|
options?: DynamicOptions<P>
|
||||||
): React.ComponentType<P> {
|
): React.ComponentType<P> {
|
||||||
const loadableFn: LoadableFn<P> = Loadable
|
|
||||||
|
|
||||||
const loadableOptions: LoadableOptions<P> = {
|
const loadableOptions: LoadableOptions<P> = {
|
||||||
// A loading component is not required, so we default it
|
// A loading component is not required, so we default it
|
||||||
loading: ({ error, isLoading, pastDelay }) => {
|
loading: ({ error, isLoading, pastDelay }) => {
|
||||||
|
@ -78,13 +60,5 @@ export default function dynamic<P = {}>(
|
||||||
loadableOptions.loader = dynamicOptions
|
loadableOptions.loader = dynamicOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.assign(loadableOptions, options)
|
return Loadable({ ...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> })
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,42 @@
|
||||||
import React from 'react'
|
import { Suspense, lazy, Fragment } from 'react'
|
||||||
import { NoSSR } from './dynamic-no-ssr'
|
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) {
|
function Loadable(options: any) {
|
||||||
const opts = Object.assign(
|
const opts = {
|
||||||
{
|
loader: null,
|
||||||
loader: null,
|
loading: null,
|
||||||
loading: null,
|
ssr: true,
|
||||||
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) {
|
function LoadableComponent(props: any) {
|
||||||
const Loading = opts.loading
|
const fallbackElement = Loading ? (
|
||||||
const fallbackElement = (
|
|
||||||
<Loading isLoading={true} pastDelay={true} error={null} />
|
<Loading isLoading={true} pastDelay={true} error={null} />
|
||||||
)
|
) : null
|
||||||
|
|
||||||
const Wrap = opts.ssr ? React.Fragment : NoSSR
|
|
||||||
const Lazy = opts.lazy
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Suspense fallback={fallbackElement}>
|
<Suspense fallback={fallbackElement}>
|
||||||
<Wrap>
|
<Wrap>
|
||||||
<Lazy {...props} />
|
<Lazy {...props} />
|
||||||
</Wrap>
|
</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