Skip to content

Commit

Permalink
refactor and simplify app dynamic components
Browse files Browse the repository at this point in the history
  • Loading branch information
huozhi committed Dec 15, 2023
1 parent 98e0a56 commit d092965
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 57 deletions.
52 changes: 13 additions & 39 deletions packages/next/src/shared/lib/app-dynamic.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,18 @@
import React from 'react'
import Loadable from './lazy-dynamic/loadable'

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
}

// 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 }
import type {
LoadableGeneratedOptions,
DynamicOptionsLoadingProps,
Loader,
LoaderComponent,
} from './lazy-dynamic/types'

export {
type LoadableGeneratedOptions,
type DynamicOptionsLoadingProps,
type Loader,
type LoaderComponent,
}

export type DynamicOptions<P = {}> = LoadableGeneratedOptions & {
Expand All @@ -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 }) => {
Expand All @@ -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 })
}
45 changes: 27 additions & 18 deletions packages/next/src/shared/lib/lazy-dynamic/loadable.tsx
Original file line number Diff line number Diff line change
@@ -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(
{
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 ? (
<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>
)
}

Expand Down
22 changes: 22 additions & 0 deletions packages/next/src/shared/lib/lazy-dynamic/types.ts
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit d092965

Please sign in to comment.