From 990f928f5158ccf7f3e8fde1a61c9365284792bf Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Sun, 7 Nov 2021 03:40:25 +0100 Subject: [PATCH] fix id generation --- packages/next/server/render.tsx | 44 ++++++++++++++++--- test/integration/react-18/app/pages/use-id.js | 5 +++ test/integration/react-18/test/basics.js | 11 +++++ 3 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 test/integration/react-18/app/pages/use-id.js diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index 53fa578287d73ab..3f11e124ef8921f 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -514,9 +514,9 @@ export async function renderToHTML( defaultLocale: renderOpts.defaultLocale, AppTree: (props: any) => { return ( - + - + ) }, defaultGetInitialProps: async ( @@ -577,6 +577,38 @@ export async function renderToHTML( ) + // The `useId` API uses the path indexes to generate an ID for each node. + // To guarantee the match of hydration, we need to ensure that the structure + // of wrapper nodes is isomorphic in server and client. + // TODO: With `enhanceApp` and `enhanceComponents` options, this approach may + // not be useful. + // https://github.com/facebook/react/pull/22644 + const Noop = () => null + const AppContainerWithIsomorphicFiberStructure = ({ + children, + }: { + children: JSX.Element + }) => { + return ( + <> + + + <> + {dev ? ( + <> + {children} + + + ) : ( + children + )} + + + + + ) + } + props = await loadGetInitialProps(App, { AppTree: ctx.AppTree, Component, @@ -976,13 +1008,13 @@ export async function renderToHTML( enhanceComponents(options, App, Component) const html = ReactDOMServer.renderToString( - + - + ) return { html, head } } @@ -1015,9 +1047,9 @@ export async function renderToHTML( ctx.err && ErrorDebug ? ( ) : ( - + - + ) const bodyResult = concurrentFeatures diff --git a/test/integration/react-18/app/pages/use-id.js b/test/integration/react-18/app/pages/use-id.js new file mode 100644 index 000000000000000..1b5f6fef28fca0f --- /dev/null +++ b/test/integration/react-18/app/pages/use-id.js @@ -0,0 +1,5 @@ +import { useId } from 'react' + +export default function Page() { + return
{useId()}
+} diff --git a/test/integration/react-18/test/basics.js b/test/integration/react-18/test/basics.js index bb3c4ad99ced4f7..d54a9b02e4dd436 100644 --- a/test/integration/react-18/test/basics.js +++ b/test/integration/react-18/test/basics.js @@ -28,4 +28,15 @@ export default (context) => { expect(content).toBe('rab') expect(nextData.dynamicIds).toBeUndefined() }) + + it('useId() values should match on hydration', async () => { + const html = await renderViaHTTP(context.appPort, '/use-id') + const $ = cheerio.load(html) + const ssrId = $('#id').text() + + const browser = await webdriver(context.appPort, '/use-id') + const csrId = await browser.eval('document.getElementById("id").innerText') + + expect(ssrId).toEqual(csrId) + }) }