Skip to content

Commit

Permalink
Use render after hydrate
Browse files Browse the repository at this point in the history
  • Loading branch information
Timer committed Nov 23, 2020
1 parent f637c8a commit 4f98808
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 1 deletion.
4 changes: 3 additions & 1 deletion packages/next/client/index.tsx
Expand Up @@ -477,6 +477,7 @@ export function renderError(renderErrorProps: RenderErrorProps) {
}

let reactRoot: any = null
let shouldUseHydrate = typeof ReactDOM.hydrate === 'function'
function renderReactElement(reactEl: JSX.Element, domEl: HTMLElement) {
if (process.env.__NEXT_REACT_MODE !== 'legacy') {
if (!reactRoot) {
Expand All @@ -494,8 +495,9 @@ function renderReactElement(reactEl: JSX.Element, domEl: HTMLElement) {
}

// The check for `.hydrate` is there to support React alternatives like preact
if (typeof ReactDOM.hydrate === 'function') {
if (shouldUseHydrate) {
ReactDOM.hydrate(reactEl, domEl, markHydrateComplete)
shouldUseHydrate = false
} else {
ReactDOM.render(reactEl, domEl, markRenderComplete)
}
Expand Down
25 changes: 25 additions & 0 deletions test/integration/hydrate-then-render/pages/_app.js
@@ -0,0 +1,25 @@
/* eslint-disable camelcase */
import App from 'next/app'

if (typeof navigator !== 'undefined') {
window.__BEACONS = window.__BEACONS || []

navigator.sendBeacon = async function () {
const args = await Promise.all(
[...arguments].map((v) => {
if (v instanceof Blob) {
return v.text()
}
return v
})
)

window.__BEACONS.push(args)
}
}

export default class MyApp extends App {}

export function reportWebVitals(data) {
navigator.sendBeacon('/test', new URLSearchParams(data).toString())
}
15 changes: 15 additions & 0 deletions test/integration/hydrate-then-render/pages/index.js
@@ -0,0 +1,15 @@
import Link from 'next/link'

const Home = () => {
return (
<div>
<h1>Foo!</h1>
<h2>bar!</h2>
<Link href="/other">
<a id="to-other">Other</a>
</Link>
</div>
)
}

export default Home
11 changes: 11 additions & 0 deletions test/integration/hydrate-then-render/pages/other.js
@@ -0,0 +1,11 @@
const Other = () => {
return (
<div>
<h1>Foo!</h1>
<h2>bar!</h2>
<div id="on-other"></div>
</div>
)
}

export default Other
37 changes: 37 additions & 0 deletions test/integration/hydrate-then-render/test/index.test.js
@@ -0,0 +1,37 @@
/* eslint-env jest */

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

const appDir = join(__dirname, '../')
let appPort
let server
jest.setTimeout(1000 * 60 * 2)

describe('hydrate/render ordering', () => {
beforeAll(async () => {
appPort = await findPort()
await nextBuild(appDir, [], {})
server = await nextStart(appDir, appPort)
})
afterAll(() => killApp(server))

it('correctly measures hydrate followed by render', async () => {
const browser = await webdriver(appPort, '/')
await browser.waitForElementByCss('#to-other')
await browser.elementByCss('#to-other').click()
await browser.waitForElementByCss('#on-other')

const beacons = (await browser.eval('window.__BEACONS'))
.map(([, value]) => Object.fromEntries(new URLSearchParams(value)))
.filter((p) => p.label === 'custom')
expect(beacons).toMatchObject([
{ name: 'Next.js-hydration' },
{ name: 'Next.js-render' },
{ name: 'Next.js-route-change-to-render' },
])

await browser.close()
})
})

0 comments on commit 4f98808

Please sign in to comment.