Skip to content

Commit

Permalink
fix: react 18 new hydration API
Browse files Browse the repository at this point in the history
  • Loading branch information
huozhi committed Jun 28, 2021
1 parent e8a9bd1 commit 9704163
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 9 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@
"pretty-bytes": "5.3.0",
"pretty-ms": "7.0.0",
"react": "17.0.2",
"react-18": "npm:react@18",
"react-dom": "17.0.2",
"react-dom-18": "npm:react-dom@18",
"react-ssr-prepass": "1.0.8",
"release": "6.3.0",
"request-promise-core": "1.1.2",
Expand Down
16 changes: 10 additions & 6 deletions packages/next/client/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -493,8 +493,10 @@ export function renderError(renderErrorProps: RenderErrorProps): Promise<any> {
})
}

const ReactDOM18: any = ReactDOM as any
let reactRoot: any = null
let shouldHydrate: boolean = typeof ReactDOM.hydrate === 'function'
let shouldHydrate: boolean =
typeof (ReactDOM.hydrate || ReactDOM18.hydrateRoot) === 'function'

function renderReactElement(
domEl: HTMLElement,
Expand All @@ -508,12 +510,14 @@ function renderReactElement(
const reactEl = fn(shouldHydrate ? markHydrateComplete : markRenderComplete)
if (process.env.__NEXT_REACT_ROOT) {
if (!reactRoot) {
reactRoot = (ReactDOM as any).createRoot(domEl, {
hydrate: shouldHydrate,
})
if (shouldHydrate && typeof ReactDOM18.hydrateRoot === 'function') {
reactRoot = ReactDOM18.hydrateRoot(domEl, reactEl)
} else {
reactRoot = ReactDOM18.createRoot(domEl, { hydrate: shouldHydrate })
reactRoot.render(reactEl)
}
shouldHydrate = false
}
reactRoot.render(reactEl)
shouldHydrate = false
} else {
// The check for `.hydrate` is there to support React alternatives like preact
if (shouldHydrate) {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions test/integration/react-18/supported/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
experimental: {
reactRoot: true,
},
}
4 changes: 4 additions & 0 deletions test/integration/react-18/supported/pages/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export default function Index() {
if (typeof window !== 'undefined') {
window.didHydrate = true
}
return 'details'
return <p>Hello</p>
}
37 changes: 36 additions & 1 deletion test/integration/react-18/test/index.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
/* eslint-env jest */

import { findPort, killApp, launchApp, runNextCommand } from 'next-test-utils'
import { join } from 'path'
import fs from 'fs-extra'
import webdriver from 'next-webdriver'
import {
findPort,
killApp,
launchApp,
runNextCommand,
nextBuild,
nextStart,
} from 'next-test-utils'

jest.setTimeout(1000 * 60 * 5)

Expand Down Expand Up @@ -67,4 +76,30 @@ describe('React 18 Support', () => {
expect(output).toMatch(UNSUPPORTED_PRERELEASE)
})
})

describe('hydration', () => {
const appDir = join(__dirname, '../prerelease')
let app
let appPort
beforeAll(async () => {
jest.mock('react', () => {
return jest.requireActual('react-18')
})
jest.mock('react-dom', () => {
return jest.requireActual('react-dom-18')
})
await fs.remove(join(appDir, '.next'))
const { stderr } = await nextBuild(appDir, [dirPrerelease], {
stderr: true,
})
console.error(stderr)
appPort = await findPort()
app = await nextStart(appDir, appPort, { stderr: true })
})
afterAll(async () => await killApp(app))
it('hydrates correctly for normal page', async () => {
const browser = await webdriver(appPort, '/')
expect(await browser.eval('window.didHydrate')).toBe(true)
})
})
})
25 changes: 25 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15632,6 +15632,23 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7, rc@^1.2.8:
minimist "^1.2.0"
strip-json-comments "~2.0.1"

"react-18@npm:react@18":
version "18.0.0-alpha-e6be2d531"
resolved "https://registry.yarnpkg.com/react/-/react-18.0.0-alpha-e6be2d531.tgz#c5cf6d5706f7b6411d36848b483a7937c94fc5c9"
integrity sha512-roZmm4MYfuGN65PiagPvhumBb3Da1FAgyj6Ti2sHwQDIxhi40PMq5V90gBSKXl7qXbaXHrtpuAtG5R+XkqpDYA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"

"react-dom-18@npm:react-dom@18":
version "18.0.0-alpha-e6be2d531"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.0.0-alpha-e6be2d531.tgz#89b1aef488a56ceac7cc0169ae02d7fbb58cbbb6"
integrity sha512-mMGiX/kyhxaUTRDt+Q5oIbpU6cPvO8Sp/kHz3cdNFviwVTHHF2YrnnD70BD2F1R0b7wAdfVVit9EfE+Rqt0sJA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler "0.21.0-alpha-e6be2d531"

react-dom@17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
Expand Down Expand Up @@ -16649,6 +16666,14 @@ saxes@^5.0.1:
dependencies:
xmlchars "^2.2.0"

scheduler@0.21.0-alpha-e6be2d531:
version "0.21.0-alpha-e6be2d531"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0-alpha-e6be2d531.tgz#fd895f7426e15166f1b16ed1d54de48243c78002"
integrity sha512-1GSnBp3em+oMz93ncjqR/7qeGArhgKaOWluZUrQW2JgHEGYISXAhGTVQhUYAiKc3F9jiq94CE1WrO0vaVzgReQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"

scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
Expand Down

0 comments on commit 9704163

Please sign in to comment.