diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index c4ec6dfe7aa5..f966b50224db 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -1,7 +1,6 @@ /* global location */ import '../build/polyfills/polyfill-module' import React, { useState } from 'react' -import ReactDOM from 'react-dom' import { HeadManagerContext } from '../shared/lib/head-manager-context' import mitt, { MittEmitter } from '../shared/lib/mitt' import { RouterContext } from '../shared/lib/router-context' @@ -41,6 +40,10 @@ import { RefreshContext } from './streaming/refresh' import { ImageConfigContext } from '../shared/lib/image-config-context' import { ImageConfigComplete } from '../shared/lib/image-config' +const ReactDOM = process.env.__NEXT_REACT_ROOT + ? require('react-dom/client') + : require('react-dom') + /// declare let __webpack_public_path__: string @@ -549,8 +552,7 @@ function renderReactElement( if (process.env.__NEXT_REACT_ROOT) { if (!reactRoot) { // Unlike with createRoot, you don't need a separate root.render() call here - const ReactDOMClient = require('react-dom/client') - reactRoot = ReactDOMClient.hydrateRoot(domEl, reactEl) + reactRoot = ReactDOM.hydrateRoot(domEl, reactEl) // TODO: Remove shouldHydrate variable when React 18 is stable as it can depend on `reactRoot` existing shouldHydrate = false } else { diff --git a/packages/next/server/next.ts b/packages/next/server/next.ts index 31f6f7502438..f2d46252a9a6 100644 --- a/packages/next/server/next.ts +++ b/packages/next/server/next.ts @@ -183,6 +183,14 @@ function createServer(options: NextServerOptions): NextServer { ) } + // Make sure env of custom server is overridden. + // Use dynamic require to make sure it's executed in it's own context. + const ReactDOMServer = require('react-dom/server.browser') + const shouldUseReactRoot = !!ReactDOMServer.renderToReadableStream + if (shouldUseReactRoot) { + ;(process.env as any).__NEXT_REACT_ROOT = 'true' + } + return new NextServer(options) } diff --git a/test/integration/custom-server/test/index.test.js b/test/integration/custom-server/test/index.test.js index 330fe4b1c4ef..8d4e2ee70311 100644 --- a/test/integration/custom-server/test/index.test.js +++ b/test/integration/custom-server/test/index.test.js @@ -132,8 +132,18 @@ describe('Custom Server', () => { try { browser = await webdriver(context.appPort, '/test-index-hmr') const text = await browser.elementByCss('#go-asset').text() + const logs = await browser.log() expect(text).toBe('Asset') + // Hydrates with react 18 is correct as expected + expect( + logs.some((log) => + log.message.includes( + 'ReactDOM.hydrate is no longer supported in React 18' + ) + ) + ).toBe(false) + indexPg.replace('Asset', 'Asset!!') await check(() => browser.elementByCss('#go-asset').text(), /Asset!!/)