From 04f1dd52b9d257fe14cce4818d6c1ecd2208b89c Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Mon, 20 Jan 2020 14:41:41 -0600 Subject: [PATCH] Delay hydration until after page is visible in development (#10164) * Delay hydration until after page is visible in development * Tweak dead-code elimination --- packages/next/client/dev/fouc.js | 5 +- packages/next/client/index.js | 10 +++- packages/next/client/next-dev.js | 7 ++- test/integration/initial-ref/pages/index.js | 31 ++++++++++++ .../initial-ref/test/index.test.js | 48 +++++++++++++++++++ 5 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 test/integration/initial-ref/pages/index.js create mode 100644 test/integration/initial-ref/test/index.test.js diff --git a/packages/next/client/dev/fouc.js b/packages/next/client/dev/fouc.js index 5aa68e4b3a88676..c67b799c9968f63 100644 --- a/packages/next/client/dev/fouc.js +++ b/packages/next/client/dev/fouc.js @@ -1,4 +1,4 @@ -export function displayContent() { +export function displayContent(callback) { // This is the fallback helper that removes Next.js' no-FOUC styles when // CSS mode is enabled. This only really activates if you haven't created // _any_ styles in your application yet. @@ -10,5 +10,8 @@ export function displayContent() { ) { x[i].parentNode.removeChild(x[i]) } + if (callback) { + callback() + } }) } diff --git a/packages/next/client/index.js b/packages/next/client/index.js index 1530d4f6077bafe..4650c74e1af3f24 100644 --- a/packages/next/client/index.js +++ b/packages/next/client/index.js @@ -200,9 +200,15 @@ export default async ({ webpackHMR: passedWebpackHMR } = {}) => { } const renderCtx = { App, Component, props, err: initialErr } - render(renderCtx) - return emitter + if (process.env.NODE_ENV === 'production') { + render(renderCtx) + return emitter + } + + if (process.env.NODE_ENV !== 'production') { + return { emitter, render, renderCtx } + } } export async function render(props) { diff --git a/packages/next/client/next-dev.js b/packages/next/client/next-dev.js index b66634870150b85..c9282b62ee5746a 100644 --- a/packages/next/client/next-dev.js +++ b/packages/next/client/next-dev.js @@ -28,7 +28,7 @@ const webpackHMR = initWebpackHMR({ assetPrefix: prefix }) window.next = next initNext({ webpackHMR }) - .then(emitter => { + .then(({ emitter, renderCtx, render }) => { initOnDemandEntries({ assetPrefix: prefix }) if (process.env.__NEXT_BUILD_INDICATOR) initializeBuildWatcher() if ( @@ -39,7 +39,10 @@ initNext({ webpackHMR }) initializePrerenderIndicator() } - displayContent() + // delay rendering until after styles have been applied in development + displayContent(() => { + render(renderCtx) + }) let lastScroll diff --git a/test/integration/initial-ref/pages/index.js b/test/integration/initial-ref/pages/index.js new file mode 100644 index 000000000000000..a0b6dab37292aba --- /dev/null +++ b/test/integration/initial-ref/pages/index.js @@ -0,0 +1,31 @@ +import React from 'react' + +class App extends React.Component { + constructor() { + super() + + this.divRef = React.createRef() + + this.state = { + refHeight: 0, + } + } + + componentDidMount() { + const refHeight = this.divRef.current.clientHeight + this.setState({ refHeight }) + } + + render() { + const { refHeight } = this.state + + return ( +
+

DOM Ref test using 9.2.0

+ {`this component is ${refHeight}px tall`} +
+ ) + } +} + +export default App diff --git a/test/integration/initial-ref/test/index.test.js b/test/integration/initial-ref/test/index.test.js new file mode 100644 index 000000000000000..a418167f7db847b --- /dev/null +++ b/test/integration/initial-ref/test/index.test.js @@ -0,0 +1,48 @@ +/* eslint-env jest */ +/* global jasmine */ +import path from 'path' +import webdriver from 'next-webdriver' +import { + nextBuild, + nextStart, + launchApp, + findPort, + waitFor, + killApp, +} from 'next-test-utils' + +jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 1 +const appDir = path.join(__dirname, '..') +let app +let appPort + +const runTest = () => { + it('Has correct initial ref values', async () => { + const browser = await webdriver(appPort, '/') + await waitFor(2000) + expect(await browser.elementByCss('#ref-val').text()).toContain('76px') + }) +} + +describe('Hydration', () => { + describe('production mode', () => { + beforeAll(async () => { + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(() => killApp(app)) + + runTest() + }) + + describe('dev mode', () => { + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(() => killApp(app)) + + runTest() + }) +})