From b0b0908244e53312b8fe9f0d882ca69fce4b1b09 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Wed, 17 Aug 2022 16:49:15 +0800 Subject: [PATCH] fix(#39609): warns about suspense and ssr --- errors/invalid-dynamic-suspense.md | 15 +++++++++++++-- packages/next/shared/lib/dynamic.tsx | 16 +++++++++++++++- test/e2e/dynamic-with-suspense/index.test.ts | 6 ++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/errors/invalid-dynamic-suspense.md b/errors/invalid-dynamic-suspense.md index 11e4d6134d73..14a3d67e2cbd 100644 --- a/errors/invalid-dynamic-suspense.md +++ b/errors/invalid-dynamic-suspense.md @@ -2,11 +2,22 @@ #### Why This Error Occurred -`` is not allowed under legacy render mode when using React older than v18. +- You are using a React version older than 18 with `{ suspense: true }`. +- You are using `{ suspense: true, ssr: false }`. #### Possible Ways to Fix It -Remove `suspense: true` option in `next/dynamic` usages. +**If you are using `{ suspense: true }` with React version older than 18** + +- You can try upgrading to React 18 or newer +- If upgrading React is not an option, remove `{ suspense: true }` from `next/dynamic` usages. + +**If you are using `{ suspense: true, ssr: false }`** + +Next.js will use `React.lazy` when `suspense` is set to true. React 18 or newer will always try to resolve the Suspense boundary on the server. This behavior can not be disabled, thus the `ssr: false` is ignored with `suspense: true`. + +- You should write code that works in both client-side and server-side. +- If rewriting the code is not an option, remove `{ suspense: true }` from `next/dynamic` usages. ### Useful Links diff --git a/packages/next/shared/lib/dynamic.tsx b/packages/next/shared/lib/dynamic.tsx index 91b747e4e1ea..8da202d259f7 100644 --- a/packages/next/shared/lib/dynamic.tsx +++ b/packages/next/shared/lib/dynamic.tsx @@ -107,11 +107,25 @@ export default function dynamic

( // Error if Fizz rendering is not enabled and `suspense` option is set to true if (!process.env.__NEXT_REACT_ROOT && loadableOptions.suspense) { - throw new Error( + console.warn( `Invalid suspense option usage in next/dynamic. Read more: https://nextjs.org/docs/messages/invalid-dynamic-suspense` ) } + /** + * 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 + * + * In the future, when React Suspense with third-party libraries is stable, we can implement a custom version of + * React.lazy that can suspense on the server-side while only loading the component on the client-side + */ + if (loadableOptions.ssr === false && loadableOptions.suspense) { + 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` + ) + } + // coming from build/babel/plugins/react-loadable-plugin.js if (loadableOptions.loadableGenerated) { loadableOptions = { diff --git a/test/e2e/dynamic-with-suspense/index.test.ts b/test/e2e/dynamic-with-suspense/index.test.ts index b0f24fb96a1e..824d3785ea92 100644 --- a/test/e2e/dynamic-with-suspense/index.test.ts +++ b/test/e2e/dynamic-with-suspense/index.test.ts @@ -49,7 +49,13 @@ suite('dynamic with suspense', () => { it('should render client-side', async () => { const browser = await webdriver(next.url, '/') + const warnings = (await browser.log()).map((log) => log.message).join('\n') + expect(await hasRedbox(browser)).toBe(false) + expect(warnings).toMatch( + /"ssr: false" is ignored by next\/dynamic because you can not enable "suspense" while disabling "ssr" at the same time/gim + ) + await browser.close() }) })