From 4cb11a21a7b9286c003fb9d16393d1504fa76d27 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Tue, 20 Apr 2021 16:13:03 +0000 Subject: [PATCH 1/7] Remove reactMode config --- .github/workflows/test_react_experimental.yml | 1 - packages/next/build/webpack-config.ts | 3 -- packages/next/client/index.tsx | 31 ++++++++++--------- .../next/next-server/server/config-shared.ts | 1 - packages/next/next-server/server/config.ts | 12 ------- 5 files changed, 16 insertions(+), 32 deletions(-) diff --git a/.github/workflows/test_react_experimental.yml b/.github/workflows/test_react_experimental.yml index 892a7dfab9985..2e3a39e4aabdf 100644 --- a/.github/workflows/test_react_experimental.yml +++ b/.github/workflows/test_react_experimental.yml @@ -29,7 +29,6 @@ jobs: # needs: build env: NEXT_TELEMETRY_DISABLED: 1 - NEXT_PRIVATE_REACT_MODE: concurrent HEADLESS: true NEXT_PRIVATE_SKIP_SIZE_TESTS: true strategy: diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 16130db67ed80..9dbd4e5d5ca6b 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1078,9 +1078,6 @@ export default async function getBaseWebpackConfig( 'process.env.__NEXT_STRICT_MODE': JSON.stringify( config.reactStrictMode ), - 'process.env.__NEXT_REACT_MODE': JSON.stringify( - config.experimental.reactMode - ), 'process.env.__NEXT_OPTIMIZE_FONTS': JSON.stringify( config.optimizeFonts && !dev ), diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index 9ebeece2521df..0ce1eeea69696 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -521,7 +521,14 @@ export function renderError(renderErrorProps: RenderErrorProps): Promise { } let reactRoot: any = null -let shouldUseHydrate: boolean = typeof ReactDOM.hydrate === 'function' +let shouldHydrate: boolean = typeof ReactDOM.hydrate === 'function' +const createRootName = + typeof (ReactDOM as any).createRoot === 'function' + ? 'createRoot' + : typeof (ReactDOM as any).unstable_createRoot === 'function' + ? 'unstable_createRoot' + : undefined + function renderReactElement( domEl: HTMLElement, fn: (cb: () => void) => JSX.Element @@ -531,24 +538,18 @@ function renderReactElement( performance.mark('beforeRender') } - const reactEl = fn( - shouldUseHydrate ? markHydrateComplete : markRenderComplete - ) - if (process.env.__NEXT_REACT_MODE !== 'legacy') { - if (!reactRoot) { - const opts = { hydrate: shouldUseHydrate } - reactRoot = - process.env.__NEXT_REACT_MODE === 'concurrent' - ? (ReactDOM as any).unstable_createRoot(domEl, opts) - : (ReactDOM as any).unstable_createBlockingRoot(domEl, opts) - } + const reactEl = fn(shouldHydrate ? markHydrateComplete : markRenderComplete) + if (createRootName) { + reactRoot = + reactRoot ?? + (ReactDOM as any)[createRootName](domEl, { hydrate: shouldHydrate }) + shouldHydrate = false reactRoot.render(reactEl) - shouldUseHydrate = false } else { // The check for `.hydrate` is there to support React alternatives like preact - if (shouldUseHydrate) { + if (shouldHydrate) { ReactDOM.hydrate(reactEl, domEl) - shouldUseHydrate = false + shouldHydrate = false } else { ReactDOM.render(reactEl, domEl) } diff --git a/packages/next/next-server/server/config-shared.ts b/packages/next/next-server/server/config-shared.ts index 1e7d88065294b..ef0d0277327c7 100644 --- a/packages/next/next-server/server/config-shared.ts +++ b/packages/next/next-server/server/config-shared.ts @@ -104,7 +104,6 @@ export const defaultConfig: NextConfig = { plugins: false, profiling: false, sprFlushToDisk: true, - reactMode: (process.env.NEXT_PRIVATE_REACT_MODE as any) || 'legacy', workerThreads: false, pageEnv: false, optimizeImages: false, diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index b8fa8597ad803..7c65d840801e7 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -13,7 +13,6 @@ import { loadEnvConfig } from '@next/env' export { DomainLocales, NextConfig, normalizeConfig } from './config-shared' const targets = ['server', 'serverless', 'experimental-serverless-trace'] -const reactModes = ['legacy', 'blocking', 'concurrent'] const experimentalWarning = execOnce(() => { Log.warn(chalk.bold('You have enabled experimental feature(s).')) @@ -435,17 +434,6 @@ export default async function loadConfig( : canonicalBase) || '' } - if ( - userConfig.experimental?.reactMode && - !reactModes.includes(userConfig.experimental.reactMode) - ) { - throw new Error( - `Specified React Mode is invalid. Provided: ${ - userConfig.experimental.reactMode - } should be one of ${reactModes.join(', ')}` - ) - } - if (hasNextSupport) { userConfig.target = process.env.NEXT_PRIVATE_TARGET || 'server' } From 5748edad438def9ffb131fde1c15d20abf73ebbb Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Tue, 20 Apr 2021 17:34:18 +0000 Subject: [PATCH 2/7] Add config + warning --- packages/next/build/webpack-config.ts | 3 +++ packages/next/client/index.tsx | 12 +++++------- packages/next/next-server/server/config-shared.ts | 2 ++ packages/next/next-server/server/config.ts | 13 +++++++++++++ 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 9dbd4e5d5ca6b..514b04c6dae7e 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1078,6 +1078,9 @@ export default async function getBaseWebpackConfig( 'process.env.__NEXT_STRICT_MODE': JSON.stringify( config.reactStrictMode ), + 'process.env.__NEXT_REACT_ROOT': JSON.stringify( + config.experimental.reactRoot + ), 'process.env.__NEXT_OPTIMIZE_FONTS': JSON.stringify( config.optimizeFonts && !dev ), diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index 0ce1eeea69696..af6ce4822e7c2 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -522,12 +522,6 @@ export function renderError(renderErrorProps: RenderErrorProps): Promise { let reactRoot: any = null let shouldHydrate: boolean = typeof ReactDOM.hydrate === 'function' -const createRootName = - typeof (ReactDOM as any).createRoot === 'function' - ? 'createRoot' - : typeof (ReactDOM as any).unstable_createRoot === 'function' - ? 'unstable_createRoot' - : undefined function renderReactElement( domEl: HTMLElement, @@ -539,7 +533,11 @@ function renderReactElement( } const reactEl = fn(shouldHydrate ? markHydrateComplete : markRenderComplete) - if (createRootName) { + if (process.env.__NEXT_REACT_ROOT) { + const createRootName = + typeof (ReactDOM as any).unstable_createRoot === 'function' + ? 'unstable_createRoot' + : 'createRoot' reactRoot = reactRoot ?? (ReactDOM as any)[createRootName](domEl, { hydrate: shouldHydrate }) diff --git a/packages/next/next-server/server/config-shared.ts b/packages/next/next-server/server/config-shared.ts index ef0d0277327c7..5eaa4b5b76f68 100644 --- a/packages/next/next-server/server/config-shared.ts +++ b/packages/next/next-server/server/config-shared.ts @@ -60,6 +60,7 @@ export type NextConfig = { [key: string]: any } & { skipValidation?: boolean } turboMode: boolean + reactRoot: boolean } } @@ -114,6 +115,7 @@ export const defaultConfig: NextConfig = { externalDir: false, serialWebpackBuild: false, turboMode: false, + reactRoot: false, }, future: { strictPostcssConfiguration: false, diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index 7c65d840801e7..b98139f25cbc7 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -9,6 +9,7 @@ import { defaultConfig, normalizeConfig } from './config-shared' import { loadWebpackHook } from './config-utils' import { ImageConfig, imageConfigDefault, VALID_LOADERS } from './image-config' import { loadEnvConfig } from '@next/env' +import ReactDOM from 'react' export { DomainLocales, NextConfig, normalizeConfig } from './config-shared' @@ -386,6 +387,18 @@ function assignDefaults(userConfig: { [key: string]: any }) { } } + if ( + typeof userConfig.experimental?.reactRoot === 'undefined' && + (typeof (ReactDOM as any).createRoot === 'function' || + typeof (ReactDOM as any).unstable_createRoot === 'function') + ) { + console.warn( + chalk.yellow.bold('Warning: ') + + 'The "reactRoot" option defaults to true based on your version of React. Please update your next.config.js if needed.' + ) + result.experimental.reactRoot = true + } + return result } From 6d40176e222b85248fcbe105dfcf751f803a5a30 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Tue, 20 Apr 2021 17:38:32 +0000 Subject: [PATCH 3/7] Tweak wording on warning --- packages/next/next-server/server/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index b98139f25cbc7..0364b7961cb93 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -394,7 +394,7 @@ function assignDefaults(userConfig: { [key: string]: any }) { ) { console.warn( chalk.yellow.bold('Warning: ') + - 'The "reactRoot" option defaults to true based on your version of React. Please update your next.config.js if needed.' + 'The "reactRoot" option defaulted to "true" based on your version of React. Please update your next.config.js if needed.' ) result.experimental.reactRoot = true } From aeac97ec593563319dc87ca1435325386b791859 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Tue, 20 Apr 2021 17:39:22 +0000 Subject: [PATCH 4/7] Tweak wording on warning --- packages/next/next-server/server/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index 0364b7961cb93..9e71907e3cf69 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -394,7 +394,7 @@ function assignDefaults(userConfig: { [key: string]: any }) { ) { console.warn( chalk.yellow.bold('Warning: ') + - 'The "reactRoot" option defaulted to "true" based on your version of React. Please update your next.config.js if needed.' + 'The "reactRoot" option defaulted to "true" based on your version of React. Please update your next.config.js.' ) result.experimental.reactRoot = true } From 905039455719e6d54b50890a3d771b79d8f499af Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Thu, 22 Apr 2021 15:47:46 +0000 Subject: [PATCH 5/7] Don't default reactRoot for 10.x --- .github/workflows/test_react_experimental.yml | 1 + packages/next/next-server/server/config-shared.ts | 2 +- packages/next/next-server/server/config.ts | 13 ------------- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test_react_experimental.yml b/.github/workflows/test_react_experimental.yml index 2e3a39e4aabdf..bbfb890544d67 100644 --- a/.github/workflows/test_react_experimental.yml +++ b/.github/workflows/test_react_experimental.yml @@ -31,6 +31,7 @@ jobs: NEXT_TELEMETRY_DISABLED: 1 HEADLESS: true NEXT_PRIVATE_SKIP_SIZE_TESTS: true + NEXT_PRIVATE_REACT_ROOT: 1 strategy: fail-fast: false matrix: diff --git a/packages/next/next-server/server/config-shared.ts b/packages/next/next-server/server/config-shared.ts index 5eaa4b5b76f68..b62ccca10c727 100644 --- a/packages/next/next-server/server/config-shared.ts +++ b/packages/next/next-server/server/config-shared.ts @@ -115,7 +115,7 @@ export const defaultConfig: NextConfig = { externalDir: false, serialWebpackBuild: false, turboMode: false, - reactRoot: false, + reactRoot: Number(process.env.NEXT_PRIVATE_REACT_ROOT) > 0, }, future: { strictPostcssConfiguration: false, diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index 9e71907e3cf69..7c65d840801e7 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -9,7 +9,6 @@ import { defaultConfig, normalizeConfig } from './config-shared' import { loadWebpackHook } from './config-utils' import { ImageConfig, imageConfigDefault, VALID_LOADERS } from './image-config' import { loadEnvConfig } from '@next/env' -import ReactDOM from 'react' export { DomainLocales, NextConfig, normalizeConfig } from './config-shared' @@ -387,18 +386,6 @@ function assignDefaults(userConfig: { [key: string]: any }) { } } - if ( - typeof userConfig.experimental?.reactRoot === 'undefined' && - (typeof (ReactDOM as any).createRoot === 'function' || - typeof (ReactDOM as any).unstable_createRoot === 'function') - ) { - console.warn( - chalk.yellow.bold('Warning: ') + - 'The "reactRoot" option defaulted to "true" based on your version of React. Please update your next.config.js.' - ) - result.experimental.reactRoot = true - } - return result } From a38b83245b835a6e71ff0d71171ba26976d34108 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Thu, 22 Apr 2021 16:06:46 +0000 Subject: [PATCH 6/7] yak shaving --- packages/next/client/index.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index b799baa956a58..a3c103f62ca15 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -534,15 +534,17 @@ function renderReactElement( const reactEl = fn(shouldHydrate ? markHydrateComplete : markRenderComplete) if (process.env.__NEXT_REACT_ROOT) { - const createRootName = - typeof (ReactDOM as any).unstable_createRoot === 'function' - ? 'unstable_createRoot' - : 'createRoot' - reactRoot = - reactRoot ?? - (ReactDOM as any)[createRootName](domEl, { hydrate: shouldHydrate }) - shouldHydrate = false + if (!reactRoot) { + const createRootName = + typeof (ReactDOM as any).unstable_createRoot === 'function' + ? 'unstable_createRoot' + : 'createRoot' + reactRoot = (ReactDOM as any)[createRootName](domEl, { + hydrate: shouldHydrate, + }) + } reactRoot.render(reactEl) + shouldHydrate = false } else { // The check for `.hydrate` is there to support React alternatives like preact if (shouldHydrate) { From 590aae917dc21c129198b7b3ba05025818c48ff4 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Fri, 23 Apr 2021 14:06:00 +0000 Subject: [PATCH 7/7] Add warning when using old reactMode --- packages/next/next-server/server/config.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index 7c65d840801e7..04b5691d5b0ab 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -35,6 +35,19 @@ function assignDefaults(userConfig: { [key: string]: any }) { delete userConfig.exportTrailingSlash } + if (typeof userConfig.experimental?.reactMode !== 'undefined') { + console.warn( + chalk.yellow.bold('Warning: ') + + 'The experimental "reactMode" option has been replaced with "reactRoot". Please update your next.config.js.' + ) + if (typeof userConfig.experimental?.reactRoot === 'undefined') { + userConfig.experimental.reactRoot = ['concurrent', 'blocking'].includes( + userConfig.experimental.reactMode + ) + } + delete userConfig.experimental.reactMode + } + const config = Object.keys(userConfig).reduce<{ [key: string]: any }>( (currentConfig, key) => { const value = userConfig[key]