Skip to content

Commit

Permalink
Refactor next dynamic (#44832)
Browse files Browse the repository at this point in the history
* Convert `loadable` to tsx for convenience
* Merge `NoSSR` into `loadable`
* Address the missing `preload` method mentioned in
#42589 (comment)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
huozhi and kodiakhq[bot] committed Jan 13, 2023
1 parent 6ef7f5e commit 98df70e
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 66 deletions.
Expand Up @@ -11,7 +11,7 @@ export function suspense() {

type Child = React.ReactElement<any, any>

export default function NoSSR({ children }: { children: Child }): Child {
export function NoSSR({ children }: { children: Child }): Child {
if (typeof window === 'undefined') {
suspense()
}
Expand Down
48 changes: 9 additions & 39 deletions packages/next/src/shared/lib/dynamic.tsx
@@ -1,17 +1,12 @@
import React, { lazy, Suspense } from 'react'
import React from 'react'
import Loadable from './loadable'
import NoSSR from './dynamic-no-ssr'

type ComponentModule<P = {}> = { default: React.ComponentType<P> }

export declare type LoaderComponent<P = {}> = Promise<
React.ComponentType<P> | ComponentModule<P>
>

type NormalizedLoader<P = {}> = () => Promise<{
default: React.ComponentType<P>
}>

export declare type Loader<P = {}> =
| (() => LoaderComponent<P>)
| LoaderComponent<P>
Expand Down Expand Up @@ -57,30 +52,6 @@ export type LoadableFn<P = {}> = (

export type LoadableComponent<P = {}> = React.ComponentType<P>

export function noSSR<P = {}>(
LoadableInitializer: NormalizedLoader<P>,
loadableOptions: DynamicOptions<P>
): React.ComponentType<P> {
// Removing webpack and modules means react-loadable won't try preloading
delete loadableOptions.webpack
delete loadableOptions.modules

const NoSSRComponent = lazy(LoadableInitializer)

const Loading = loadableOptions.loading!
const fallback = (
<Loading error={null} isLoading pastDelay={false} timedOut={false} />
)

return (props: any) => (
<Suspense fallback={fallback}>
<NoSSR>
<NoSSRComponent {...props} />
</NoSSR>
</Suspense>
)
}

export default function dynamic<P = {}>(
dynamicOptions: DynamicOptions<P> | Loader<P>,
options?: DynamicOptions<P>
Expand Down Expand Up @@ -127,26 +98,25 @@ export default function dynamic<P = {}>(
loadableOptions = { ...loadableOptions, ...options }

const loaderFn = loadableOptions.loader as () => LoaderComponent<P>
const loader = () => loaderFn().then(convertModule)
const loader = () =>
loaderFn != null
? loaderFn().then(convertModule)
: Promise.resolve(convertModule(() => null))

// coming from build/babel/plugins/react-loadable-plugin.js
if (loadableOptions.loadableGenerated) {
loadableOptions = {
...loadableOptions,
...loadableOptions.loadableGenerated,
loader,
}
delete loadableOptions.loadableGenerated
}

// support for disabling server side rendering, eg: dynamic(() => import('../hello-world'), {ssr: false}).
if (typeof loadableOptions.ssr === 'boolean') {
if (!loadableOptions.ssr) {
delete loadableOptions.ssr
return noSSR(loader, loadableOptions)
}
delete loadableOptions.ssr
if (typeof loadableOptions.ssr === 'boolean' && !loadableOptions.ssr) {
delete loadableOptions.webpack
delete loadableOptions.modules
}

return loadableFn(loadableOptions)
return loadableFn({ ...loadableOptions, loader: loader as Loader<P> })
}
15 changes: 0 additions & 15 deletions packages/next/src/shared/lib/loadable.d.ts

This file was deleted.

Expand Up @@ -23,6 +23,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
// Modified to be compatible with webpack 4 / Next.js

import React from 'react'
import { NoSSR } from './dynamic-no-ssr'
import { LoadableContext } from './loadable-context'

const ALL_INITIALIZERS: any[] = []
Expand Down Expand Up @@ -62,6 +63,7 @@ function createLoadableComponent(loadFn: any, options: any) {
timeout: null,
webpack: null,
modules: null,
ssr: true,
},
options
)
Expand Down Expand Up @@ -117,21 +119,24 @@ function createLoadableComponent(loadFn: any, options: any) {
})
}
}

function LoadableComponent(props: any) {
useLoadableModule()

const fallbackElement = React.createElement(opts.loading, {
isLoading: true,
pastDelay: true,
error: null,
})
const Loading = opts.loading
const fallbackElement = (
<Loading isLoading={true} pastDelay={true} error={null} />
)

const Wrap = opts.ssr ? React.Fragment : NoSSR
const Lazy = opts.lazy

return React.createElement(
React.Suspense,
{
fallback: fallbackElement,
},
React.createElement(opts.lazy, props)
return (
<React.Suspense fallback={fallbackElement}>
<Wrap>
<Lazy {...props} />
</Wrap>
</React.Suspense>
)
}

Expand Down
4 changes: 4 additions & 0 deletions test/unit/next-dynamic.test.tsx
Expand Up @@ -8,6 +8,10 @@ import dynamic from 'next/dynamic'
describe('next/dynamic', () => {
it('test dynamic with jest', () => {
const App = dynamic(() => import('./fixtures/stub-components/hello'))

// @ts-ignore
expect(App.preload).toBeDefined()

act(() => {
const { unmount } = render(<App />)
unmount()
Expand Down

0 comments on commit 98df70e

Please sign in to comment.