Skip to content

Commit

Permalink
Support new hydrate API in latest react 18 alpha release (#26664)
Browse files Browse the repository at this point in the history
* fix: react 18 new hydration API

* support react 18

* compat latest react only, fix resolved version

* fix tests

* Some changes based on reactwg/react-18#5

* fix test

Co-authored-by: Tim Neutkens <timneutkens@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
3 people committed Jun 30, 2021
1 parent 5b9ad8d commit ddc0e79
Show file tree
Hide file tree
Showing 11 changed files with 83 additions and 11 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@next",
"react-dom": "17.0.2",
"react-dom-18": "npm:react-dom@next",
"react-ssr-prepass": "1.0.8",
"release": "6.3.0",
"request-promise-core": "1.1.2",
Expand Down
14 changes: 8 additions & 6 deletions packages/next/client/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,8 @@ export function renderError(renderErrorProps: RenderErrorProps): Promise<any> {
}

let reactRoot: any = null
let shouldHydrate: boolean = typeof ReactDOM.hydrate === 'function'
// On initial render a hydrate should always happen
let shouldHydrate: boolean = true

function renderReactElement(
domEl: HTMLElement,
Expand All @@ -503,12 +504,13 @@ function renderReactElement(
const reactEl = fn(shouldHydrate ? markHydrateComplete : markRenderComplete)
if (process.env.__NEXT_REACT_ROOT) {
if (!reactRoot) {
reactRoot = (ReactDOM as any).createRoot(domEl, {
hydrate: shouldHydrate,
})
// Unlike with createRoot, you don't need a separate root.render() call here
reactRoot = (ReactDOM as any).hydrateRoot(domEl, reactEl)
// TODO: Remove shouldHydrate variable when React 18 is stable as it can depend on `reactRoot` existing
shouldHydrate = false
} else {
reactRoot.render(reactEl)
}
reactRoot.render(reactEl)
shouldHydrate = false
} else {
// The check for `.hydrate` is there to support React alternatives like preact
if (shouldHydrate) {
Expand Down
2 changes: 1 addition & 1 deletion test/integration/build-output/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ describe('Build Output', () => {
expect(parseFloat(err404Size)).toBeCloseTo(gz ? 3.17 : 8.51, 1)
expect(err404Size.endsWith('kB')).toBe(true)

expect(parseFloat(err404FirstLoad)).toBeCloseTo(gz ? 66.9 : 205, 1)
expect(parseFloat(err404FirstLoad)).toBeCloseTo(gz ? 66.9 : 204, 1)
expect(err404FirstLoad.endsWith('kB')).toBe(true)

expect(parseFloat(sharedByAll)).toBeCloseTo(gz ? 63.7 : 196, 1)
Expand Down
2 changes: 1 addition & 1 deletion test/integration/fallback-modules/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe('Build Output', () => {
expect(indexSize.endsWith('kB')).toBe(true)

expect(parseFloat(indexFirstLoad)).toBeLessThanOrEqual(
process.env.NEXT_PRIVATE_TEST_WEBPACK4_MODE ? 68 : 67.9
process.env.NEXT_PRIVATE_TEST_WEBPACK4_MODE ? 68.1 : 67.9
)
expect(parseFloat(indexFirstLoad)).toBeGreaterThanOrEqual(60)
expect(indexFirstLoad.endsWith('kB')).toBe(true)
Expand Down
14 changes: 14 additions & 0 deletions test/integration/react-18/prerelease/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
webpack(config) {
const { alias } = config.resolve
// FIXME: resolving react/jsx-runtime https://github.com/facebook/react/issues/20235
alias['react/jsx-dev-runtime'] = require.resolve('react/jsx-dev-runtime.js')
alias['react/jsx-runtime'] = require.resolve('react/jsx-runtime.js')

// Use react 18
alias['react'] = require.resolve('react-18')
alias['react-dom'] = require.resolve('react-dom-18')

return config
},
}

This file was deleted.

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

1 change: 1 addition & 0 deletions test/integration/react-18/prerelease/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"dependencies": {
"react": "*",
"react-dom": "*"
}
}
3 changes: 3 additions & 0 deletions test/integration/react-18/prerelease/pages/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export default function Index() {
if (typeof window !== 'undefined') {
window.didHydrate = true
}
return <p>Hello</p>
}
28 changes: 27 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,21 @@ describe('React 18 Support', () => {
expect(output).toMatch(UNSUPPORTED_PRERELEASE)
})
})

describe('hydration', () => {
const appDir = join(__dirname, '../prerelease')
let app
let appPort
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
await nextBuild(appDir, [dirPrerelease])
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
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@next":
version "18.0.0-alpha-73ffce1b6-20210624"
resolved "https://registry.yarnpkg.com/react/-/react-18.0.0-alpha-73ffce1b6-20210624.tgz#d9fb8700c6fad8de752ec0427f2ae3a941eea951"
integrity sha512-Qaj2vhrMlYc169Yh0gXBB7WeKWMeIVx99JnuouuT71Jku2Cly9TxAWurc+h6PSgz/qjjmDA2NOtHCb6mGlmzGA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"

"react-dom-18@npm:react-dom@next":
version "18.0.0-alpha-73ffce1b6-20210624"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.0.0-alpha-73ffce1b6-20210624.tgz#3d789e4f3446abc685a7754c8dc74dea0ffb4247"
integrity sha512-TgA+VhVas3mJdhy6AQLXnPzBN2JeNKC7EGhLKU11XOxUODCGQ94nyT04i1ta2R3Fv0QevMLp0Wb5hccan0wMEg==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler "0.21.0-alpha-73ffce1b6-20210624"

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-73ffce1b6-20210624:
version "0.21.0-alpha-73ffce1b6-20210624"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0-alpha-73ffce1b6-20210624.tgz#6fff95e26af73cfaa365b68fa3b68c4c66dfe347"
integrity sha512-7SXTiepGRo63F5Yp/fxLhZDYi5TInsqjnMTYF6GwtunUGAwyuK4V/AFiF0Q1gtB32U/e+C+OE4SSj9LtBYBjYw==
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 ddc0e79

Please sign in to comment.