Skip to content

Commit

Permalink
fix id generation
Browse files Browse the repository at this point in the history
  • Loading branch information
shuding committed Nov 7, 2021
1 parent 5e185fc commit 990f928
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 6 deletions.
44 changes: 38 additions & 6 deletions packages/next/server/render.tsx
Expand Up @@ -514,9 +514,9 @@ export async function renderToHTML(
defaultLocale: renderOpts.defaultLocale,
AppTree: (props: any) => {
return (
<AppContainer>
<AppContainerWithIsomorphicFiberStructure>
<App {...props} Component={Component} router={router} />
</AppContainer>
</AppContainerWithIsomorphicFiberStructure>
)
},
defaultGetInitialProps: async (
Expand Down Expand Up @@ -577,6 +577,38 @@ export async function renderToHTML(
</RouterContext.Provider>
)

// 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 (
<>
<Noop />
<AppContainer>
<>
{dev ? (
<>
{children}
<Noop />
</>
) : (
children
)}
<Noop />
</>
</AppContainer>
</>
)
}

props = await loadGetInitialProps(App, {
AppTree: ctx.AppTree,
Component,
Expand Down Expand Up @@ -976,13 +1008,13 @@ export async function renderToHTML(
enhanceComponents(options, App, Component)

const html = ReactDOMServer.renderToString(
<AppContainer>
<AppContainerWithIsomorphicFiberStructure>
<EnhancedApp
Component={EnhancedComponent}
router={router}
{...props}
/>
</AppContainer>
</AppContainerWithIsomorphicFiberStructure>
)
return { html, head }
}
Expand Down Expand Up @@ -1015,9 +1047,9 @@ export async function renderToHTML(
ctx.err && ErrorDebug ? (
<ErrorDebug error={ctx.err} />
) : (
<AppContainer>
<AppContainerWithIsomorphicFiberStructure>
<App {...props} Component={Component} router={router} />
</AppContainer>
</AppContainerWithIsomorphicFiberStructure>
)

const bodyResult = concurrentFeatures
Expand Down
5 changes: 5 additions & 0 deletions test/integration/react-18/app/pages/use-id.js
@@ -0,0 +1,5 @@
import { useId } from 'react'

export default function Page() {
return <div id="id">{useId()}</div>
}
11 changes: 11 additions & 0 deletions test/integration/react-18/test/basics.js
Expand Up @@ -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)
})
}

0 comments on commit 990f928

Please sign in to comment.