Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace experimental reactMode with reactRoot #24280

Merged
merged 12 commits into from
Apr 24, 2021
2 changes: 1 addition & 1 deletion .github/workflows/test_react_experimental.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ jobs:
# needs: build
env:
NEXT_TELEMETRY_DISABLED: 1
NEXT_PRIVATE_REACT_MODE: concurrent
HEADLESS: true
NEXT_PRIVATE_SKIP_SIZE_TESTS: true
NEXT_PRIVATE_REACT_ROOT: 1
strategy:
fail-fast: false
matrix:
Expand Down
4 changes: 2 additions & 2 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1048,8 +1048,8 @@ 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_REACT_ROOT': JSON.stringify(
config.experimental.reactRoot
),
'process.env.__NEXT_OPTIMIZE_FONTS': JSON.stringify(
config.optimizeFonts && !dev
Expand Down
27 changes: 14 additions & 13 deletions packages/next/client/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,8 @@ export function renderError(renderErrorProps: RenderErrorProps): Promise<any> {
}

let reactRoot: any = null
let shouldUseHydrate: boolean = typeof ReactDOM.hydrate === 'function'
let shouldHydrate: boolean = typeof ReactDOM.hydrate === 'function'

function renderReactElement(
domEl: HTMLElement,
fn: (cb: () => void) => JSX.Element
Expand All @@ -504,24 +505,24 @@ function renderReactElement(
performance.mark('beforeRender')
}

const reactEl = fn(
shouldUseHydrate ? markHydrateComplete : markRenderComplete
)
if (process.env.__NEXT_REACT_MODE !== 'legacy') {
const reactEl = fn(shouldHydrate ? markHydrateComplete : markRenderComplete)
if (process.env.__NEXT_REACT_ROOT) {
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 createRootName =
typeof (ReactDOM as any).unstable_createRoot === 'function'
? 'unstable_createRoot'
: 'createRoot'
reactRoot = (ReactDOM as any)[createRootName](domEl, {
hydrate: shouldHydrate,
})
}
reactRoot.render(reactEl)
shouldUseHydrate = false
shouldHydrate = 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)
}
Expand Down
3 changes: 2 additions & 1 deletion packages/next/next-server/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export type NextConfig = { [key: string]: any } & {
skipValidation?: boolean
}
turboMode: boolean
reactRoot: boolean
}
}

Expand Down Expand Up @@ -104,7 +105,6 @@ export const defaultConfig: NextConfig = {
plugins: false,
profiling: false,
sprFlushToDisk: true,
reactMode: (process.env.NEXT_PRIVATE_REACT_MODE as any) || 'legacy',
devknoll marked this conversation as resolved.
Show resolved Hide resolved
workerThreads: false,
pageEnv: false,
optimizeImages: false,
Expand All @@ -115,6 +115,7 @@ export const defaultConfig: NextConfig = {
externalDir: false,
serialWebpackBuild: false,
turboMode: false,
reactRoot: Number(process.env.NEXT_PRIVATE_REACT_ROOT) > 0,
},
future: {
strictPostcssConfiguration: false,
Expand Down
25 changes: 13 additions & 12 deletions packages/next/next-server/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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).'))
Expand All @@ -36,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]
Expand Down Expand Up @@ -435,17 +447,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'
}
Expand Down