Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(#40388): next/dynamic should only add default loading without suspense #40397

Merged
merged 8 commits into from Sep 9, 2022
Merged
53 changes: 29 additions & 24 deletions packages/next/shared/lib/dynamic.tsx
Expand Up @@ -65,29 +65,33 @@ export default function dynamic<P = {}>(
options?: DynamicOptions<P>
): React.ComponentType<P> {
let loadableFn: LoadableFn<P> = Loadable
let loadableOptions: LoadableOptions<P> = {
// A loading component is not required, so we default it
loading: ({ error, isLoading, pastDelay }) => {
if (!pastDelay) return null
if (process.env.NODE_ENV === 'development') {
if (isLoading) {

let loadableOptions: LoadableOptions<P> = options?.suspense
huozhi marked this conversation as resolved.
Show resolved Hide resolved
? {}
: // only provide a default loading component when suspense is disabled
{
// A loading component is not required, so we default it
loading: ({ error, isLoading, pastDelay }) => {
if (!pastDelay) return null
if (process.env.NODE_ENV === 'development') {
if (isLoading) {
return null
}
if (error) {
return (
<p>
{error.message}
<br />
{error.stack}
</p>
)
}
}

return null
}
if (error) {
return (
<p>
{error.message}
<br />
{error.stack}
</p>
)
}
},
}

return null
},
}

// Support for direct import(), eg: dynamic(import('../hello-world'))
// Note that this is only kept for the edge case where someone is passing in a promise as first argument
// The react-loadable babel plugin will turn dynamic(import('../hello-world')) into dynamic(() => import('../hello-world'))
Expand All @@ -112,8 +116,8 @@ export default function dynamic<P = {}>(
)
}

if (process.env.NODE_ENV !== 'production') {
if (loadableOptions.suspense) {
if (loadableOptions.suspense) {
if (process.env.NODE_ENV !== 'production') {
/**
* TODO: Currently, next/dynamic will opt-in to React.lazy if { suspense: true } is used
* React 18 will always resolve the Suspense boundary on the server-side, effectively ignoring the ssr option
Expand All @@ -122,19 +126,20 @@ export default function dynamic<P = {}>(
* React.lazy that can suspense on the server-side while only loading the component on the client-side
*/
if (loadableOptions.ssr === false) {
loadableOptions.ssr = true
console.warn(
`"ssr: false" is ignored by next/dynamic because you can not enable "suspense" while disabling "ssr" at the same time. Read more: https://nextjs.org/docs/messages/invalid-dynamic-suspense`
)
}

if (loadableOptions.loading != null) {
loadableOptions.loading = undefined
console.warn(
`"loading" is ignored by next/dynamic because you have enabled "suspense". Place your loading element in your suspense boundary's "fallback" prop instead. Read more: https://nextjs.org/docs/messages/invalid-dynamic-suspense`
)
}
}

delete loadableOptions.ssr
delete loadableOptions.loading
}

// coming from build/babel/plugins/react-loadable-plugin.js
Expand Down
6 changes: 6 additions & 0 deletions test/integration/next-dynamic/test/index.test.js
Expand Up @@ -31,6 +31,12 @@ function runTests() {
// Failure case is 'Index<!-- -->3<!-- --><!-- -->'
expect(text).toBe('Index<!-- -->1<!-- -->2<!-- -->3<!-- -->4<!-- -->4')
expect(await browser.eval('window.caughtErrors')).toBe('')

// should not print "invalid-dynamic-suspense" warning in browser's console
const logs = (await browser.log()).map((log) => log.message).join('\n')
expect(logs).not.toContain(
'https://nextjs.org/docs/messages/invalid-dynamic-suspense'
)
})
}

Expand Down