Skip to content

Commit

Permalink
Remove react root condition and always use concurrent mode (#42141)
Browse files Browse the repository at this point in the history
Removing the `hasReactRoot` condition and `__NEXT_REACT_ROOT` env var since next 13 requires latest react 18 to be installed, all the react 17 (non concurrent mode) compatible code can be dropped now.
  • Loading branch information
huozhi committed Oct 29, 2022
1 parent 6e734d9 commit 6028a7a
Show file tree
Hide file tree
Showing 18 changed files with 105 additions and 259 deletions.
7 changes: 0 additions & 7 deletions packages/next/bin/next.ts
Expand Up @@ -95,13 +95,6 @@ if (process.env.NODE_ENV) {
;(process.env as any).NODE_ENV = process.env.NODE_ENV || defaultEnv
;(process.env as any).NEXT_RUNTIME = 'nodejs'

// In node.js runtime, react has to be required after NODE_ENV is set,
// so that the correct dev/prod bundle could be loaded into require.cache.
const { shouldUseReactRoot } = require('../server/utils')
if (shouldUseReactRoot) {
;(process.env as any).__NEXT_REACT_ROOT = 'true'
}

// x-ref: https://github.com/vercel/next.js/pull/34688#issuecomment-1047994505
if (process.versions.pnp === '3') {
const nodeVersionParts = process.versions.node
Expand Down
3 changes: 0 additions & 3 deletions packages/next/build/index.ts
Expand Up @@ -274,8 +274,6 @@ export default async function build(
setGlobal('phase', PHASE_PRODUCTION_BUILD)
setGlobal('distDir', distDir)

const hasReactRoot = !!process.env.__NEXT_REACT_ROOT

const { target } = config
const buildId: string = await nextBuildSpan
.traceChild('generate-buildid')
Expand Down Expand Up @@ -910,7 +908,6 @@ export default async function build(
const commonWebpackOptions = {
buildId,
config,
hasReactRoot,
pagesDir,
reactProductionProfiling,
rewrites,
Expand Down
28 changes: 8 additions & 20 deletions packages/next/build/webpack-config.ts
@@ -1,3 +1,4 @@
import React from 'react'
import ReactRefreshWebpackPlugin from 'next/dist/compiled/@next/react-refresh-utils/dist/ReactRefreshWebpackPlugin'
import chalk from 'next/dist/compiled/chalk'
import crypto from 'crypto'
Expand Down Expand Up @@ -69,6 +70,10 @@ const NEXT_PROJECT_ROOT_DIST_CLIENT = path.join(
'client'
)

if (parseInt(React.version) < 18) {
throw new Error('Next.js requires react >= 18.2.0 to be installed.')
}

const babelIncludeRegexes: RegExp[] = [
/next[\\/]dist[\\/](esm[\\/])?shared[\\/]lib/,
/next[\\/]dist[\\/](esm[\\/])?client/,
Expand Down Expand Up @@ -161,7 +166,6 @@ export function getDefineEnv({
distDir,
isClient,
hasRewrites,
hasReactRoot,
isNodeServer,
isEdgeServer,
middlewareMatchers,
Expand All @@ -170,7 +174,6 @@ export function getDefineEnv({
distDir: string
isClient?: boolean
hasRewrites?: boolean
hasReactRoot?: boolean
isNodeServer?: boolean
isEdgeServer?: boolean
middlewareMatchers?: MiddlewareMatcher[]
Expand Down Expand Up @@ -255,7 +258,6 @@ export function getDefineEnv({
: false
: config.reactStrictMode
),
'process.env.__NEXT_REACT_ROOT': JSON.stringify(hasReactRoot),
'process.env.__NEXT_OPTIMIZE_FONTS': JSON.stringify(
!dev && config.optimizeFonts
),
Expand Down Expand Up @@ -569,7 +571,6 @@ export default async function getBaseWebpackConfig(
compilerType,
dev = false,
entrypoints,
hasReactRoot,
isDevFallback = false,
pagesDir,
reactProductionProfiling = false,
Expand All @@ -584,7 +585,6 @@ export default async function getBaseWebpackConfig(
compilerType: CompilerNameValues
dev?: boolean
entrypoints: webpack.EntryObject
hasReactRoot: boolean
isDevFallback?: boolean
pagesDir?: string
reactProductionProfiling?: boolean
Expand All @@ -608,19 +608,8 @@ export default async function getBaseWebpackConfig(
rewrites.fallback.length > 0

const hasAppDir = !!config.experimental.appDir && !!appDir
const hasConcurrentFeatures = hasReactRoot
const hasServerComponents = hasAppDir

// Only error in first one compiler (client) once
if (isClient) {
if (!hasReactRoot) {
throw new Error('Next.js requires React 18.2.0 to be installed.')
}
}

const disableOptimizedLoading = hasConcurrentFeatures
? true
: config.experimental.disableOptimizedLoading
const disableOptimizedLoading = true

if (isClient) {
if (config.experimental.runtime === SERVER_RUNTIME.edge) {
Expand Down Expand Up @@ -1973,7 +1962,6 @@ export default async function getBaseWebpackConfig(
distDir,
isClient,
hasRewrites,
hasReactRoot,
isNodeServer,
isEdgeServer,
middlewareMatchers,
Expand All @@ -1983,7 +1971,7 @@ export default async function getBaseWebpackConfig(
new ReactLoadablePlugin({
filename: REACT_LOADABLE_MANIFEST,
pagesDir,
runtimeAsset: hasConcurrentFeatures
runtimeAsset: true
? `server/${MIDDLEWARE_REACT_LOADABLE_MANIFEST}.js`
: undefined,
dev,
Expand Down Expand Up @@ -2056,7 +2044,7 @@ export default async function getBaseWebpackConfig(
buildId,
rewrites,
isDevFallback,
exportRuntime: hasConcurrentFeatures,
exportRuntime: true,
appDirEnabled: hasAppDir,
}),
new ProfilingPlugin({ runWebpackSpan }),
Expand Down
12 changes: 3 additions & 9 deletions packages/next/client/image.tsx
Expand Up @@ -794,19 +794,13 @@ export default function Image({
}
}

let imageSrcSetPropName = 'imagesrcset'
let imageSizesPropName = 'imagesizes'
if (process.env.__NEXT_REACT_ROOT) {
imageSrcSetPropName = 'imageSrcSet'
imageSizesPropName = 'imageSizes'
}
const linkProps: React.DetailedHTMLProps<
React.LinkHTMLAttributes<HTMLLinkElement>,
HTMLLinkElement
> = {
// Note: imagesrcset and imagesizes are not in the link element type with react 17.
[imageSrcSetPropName]: imgAttributes.srcSet,
[imageSizesPropName]: imgAttributes.sizes,
// @ts-expect-error upgrade react types to react 18
imageSrcSet: imgAttributes.srcSet,
imageSizes: imgAttributes.sizes,
crossOrigin: rest.crossOrigin,
}

Expand Down
36 changes: 12 additions & 24 deletions packages/next/client/index.tsx
@@ -1,10 +1,12 @@
/* global location */
import '../build/polyfills/polyfill-module'
import type Router from '../shared/lib/router/router'
import React from 'react'
// @ts-expect-error upgrade react types to react 18
import ReactDOM from 'react-dom/client'
import { HeadManagerContext } from '../shared/lib/head-manager-context'
import mitt, { MittEmitter } from '../shared/lib/mitt'
import { RouterContext } from '../shared/lib/router-context'
import type Router from '../shared/lib/router/router'
import {
AppComponent,
AppProps,
Expand Down Expand Up @@ -35,10 +37,6 @@ import { ImageConfigComplete } from '../shared/lib/image-config'
import { removeBasePath } from './remove-base-path'
import { hasBasePath } from './has-base-path'

const ReactDOM = process.env.__NEXT_REACT_ROOT
? require('react-dom/client')
: require('react-dom')

/// <reference types="react-dom/experimental" />

declare let __webpack_public_path__: string
Expand Down Expand Up @@ -492,26 +490,16 @@ function renderReactElement(
}

const reactEl = fn(shouldHydrate ? markHydrateComplete : markRenderComplete)
if (process.env.__NEXT_REACT_ROOT) {
if (!reactRoot) {
// Unlike with createRoot, you don't need a separate root.render() call here
reactRoot = ReactDOM.hydrateRoot(domEl, reactEl)
// TODO: Remove shouldHydrate variable when React 18 is stable as it can depend on `reactRoot` existing
shouldHydrate = false
} else {
const startTransition = (React as any).startTransition
startTransition(() => {
reactRoot.render(reactEl)
})
}
if (!reactRoot) {
// Unlike with createRoot, you don't need a separate root.render() call here
reactRoot = ReactDOM.hydrateRoot(domEl, reactEl)
// TODO: Remove shouldHydrate variable when React 18 is stable as it can depend on `reactRoot` existing
shouldHydrate = false
} else {
// The check for `.hydrate` is there to support React alternatives like preact
if (shouldHydrate) {
ReactDOM.hydrate(reactEl, domEl)
shouldHydrate = false
} else {
ReactDOM.render(reactEl, domEl)
}
const startTransition = (React as any).startTransition
startTransition(() => {
reactRoot.render(reactEl)
})
}
}

Expand Down
12 changes: 3 additions & 9 deletions packages/next/client/legacy/image.tsx
Expand Up @@ -969,19 +969,13 @@ export default function Image({
}
}

let imageSrcSetPropName = 'imagesrcset'
let imageSizesPropName = 'imagesizes'
if (process.env.__NEXT_REACT_ROOT) {
imageSrcSetPropName = 'imageSrcSet'
imageSizesPropName = 'imageSizes'
}
const linkProps: React.DetailedHTMLProps<
React.LinkHTMLAttributes<HTMLLinkElement>,
HTMLLinkElement
> = {
// Note: imagesrcset and imagesizes are not in the link element type with react 17.
[imageSrcSetPropName]: imgAttributes.srcSet,
[imageSizesPropName]: imgAttributes.sizes,
// @ts-expect-error upgrade react types to react 18
imageSrcSet: imgAttributes.srcSet,
imageSizes: imgAttributes.sizes,
crossOrigin: rest.crossOrigin,
}

Expand Down
4 changes: 2 additions & 2 deletions packages/next/package.json
Expand Up @@ -84,8 +84,8 @@
"peerDependencies": {
"fibers": ">= 3.1.0",
"node-sass": "^6.0.0 || ^7.0.0",
"react": "^18.0.0-0",
"react-dom": "^18.0.0-0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.3.0"
},
"peerDependenciesMeta": {
Expand Down
2 changes: 0 additions & 2 deletions packages/next/server/app-render.tsx
Expand Up @@ -23,7 +23,6 @@ import {
streamToString,
} from './node-web-streams-helper'
import { ESCAPE_REGEX, htmlEscapeJsonString } from './htmlescape'
import { shouldUseReactRoot } from './utils'
import { matchSegment } from '../client/components/match-segments'
import {
FlightCSSManifest,
Expand Down Expand Up @@ -671,7 +670,6 @@ function headersWithoutFlight(headers: IncomingHttpHeaders) {
}

async function renderToString(element: React.ReactElement) {
if (!shouldUseReactRoot) return ReactDOMServer.renderToString(element)
const renderStream = await ReactDOMServer.renderToReadableStream(element)
await renderStream.allReady
return streamToString(renderStream)
Expand Down
6 changes: 2 additions & 4 deletions packages/next/server/base-server.ts
Expand Up @@ -1150,10 +1150,8 @@ export default abstract class Server<ServerOptions extends Options = Options> {
const isBotRequest = isBot(req.headers['user-agent'] || '')
const isSupportedDocument =
typeof components.Document?.getInitialProps !== 'function' ||
// When concurrent features is enabled, the built-in `Document`
// component also supports dynamic HTML.
(!!process.env.__NEXT_REACT_ROOT &&
NEXT_BUILTIN_DOCUMENT in components.Document)
// The built-in `Document` component also supports dynamic HTML for concurrent mode.
NEXT_BUILTIN_DOCUMENT in components.Document

// Disable dynamic HTML in cases that we know it won't be generated,
// so that we can continue generating a cache key when possible.
Expand Down
6 changes: 1 addition & 5 deletions packages/next/server/dev/hot-reloader.ts
Expand Up @@ -163,7 +163,6 @@ export default class HotReloader {
private webpackHotMiddleware?: WebpackHotMiddleware
private config: NextConfigComplete
public hasServerComponents: boolean
public hasReactRoot: boolean
public clientStats: webpack.Stats | null
public serverStats: webpack.Stats | null
public edgeServerStats: webpack.Stats | null
Expand Down Expand Up @@ -216,8 +215,7 @@ export default class HotReloader {
this.serverPrevDocumentHash = null

this.config = config
this.hasReactRoot = !!process.env.__NEXT_REACT_ROOT
this.hasServerComponents = this.hasReactRoot && !!this.appDir
this.hasServerComponents = !!this.appDir
this.previewProps = previewProps
this.rewrites = rewrites
this.hotReloaderSpan = trace('hot-reloader', undefined, {
Expand Down Expand Up @@ -456,7 +454,6 @@ export default class HotReloader {
dev: true,
buildId: this.buildId,
config: this.config,
hasReactRoot: this.hasReactRoot,
pagesDir: this.pagesDir,
rewrites: this.rewrites,
runWebpackSpan: this.hotReloaderSpan,
Expand Down Expand Up @@ -521,7 +518,6 @@ export default class HotReloader {
pageExtensions: this.config.pageExtensions,
})
).client,
hasReactRoot: this.hasReactRoot,
})
const fallbackCompiler = webpack(fallbackConfig)

Expand Down
1 change: 0 additions & 1 deletion packages/next/server/dev/next-dev-server.ts
Expand Up @@ -547,7 +547,6 @@ export default class DevServer extends Server {
distDir: this.distDir,
isClient,
hasRewrites,
hasReactRoot: this.hotReloader?.hasReactRoot,
isNodeServer,
isEdgeServer,
})
Expand Down
5 changes: 0 additions & 5 deletions packages/next/server/next-server.ts
Expand Up @@ -94,15 +94,10 @@ import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-
import { getNextPathnameInfo } from '../shared/lib/router/utils/get-next-pathname-info'
import { getClonableBody } from './body-streams'
import { checkIsManualRevalidate } from './api-utils'
import { shouldUseReactRoot } from './utils'
import ResponseCache from './response-cache'
import { IncrementalCache } from './lib/incremental-cache'
import { normalizeAppPath } from '../shared/lib/router/utils/app-paths'

if (shouldUseReactRoot) {
;(process.env as any).__NEXT_REACT_ROOT = 'true'
}

import { renderToHTMLOrFlight as appRenderToHTMLOrFlight } from './app-render'

export * from './base-server'
Expand Down
5 changes: 0 additions & 5 deletions packages/next/server/next.ts
Expand Up @@ -11,7 +11,6 @@ import { PHASE_DEVELOPMENT_SERVER } from '../shared/lib/constants'
import { PHASE_PRODUCTION_SERVER } from '../shared/lib/constants'
import { IncomingMessage, ServerResponse } from 'http'
import { NextUrlWithParsedQuery } from './request-meta'
import { shouldUseReactRoot } from './utils'
import {
loadRequireHook,
overrideBuiltInReactPackages,
Expand Down Expand Up @@ -211,10 +210,6 @@ function createServer(options: NextServerOptions): NextServer {
)
}

if (shouldUseReactRoot) {
;(process.env as any).__NEXT_REACT_ROOT = 'true'
}

return new NextServer(options)
}

Expand Down

0 comments on commit 6028a7a

Please sign in to comment.