Skip to content

Commit

Permalink
feat(gatsby): enable concurrent features (#31394) (#31580)
Browse files Browse the repository at this point in the history
Co-authored-by: Lennart <lekoarts@gmail.com>
(cherry picked from commit 3457163)

Co-authored-by: Ward Peeters <ward@coding-tech.com>
  • Loading branch information
GatsbyJS Bot and wardpeet committed May 25, 2021
1 parent 1960552 commit d543723
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 32 deletions.
94 changes: 71 additions & 23 deletions packages/gatsby/cache-dir/app.js
Expand Up @@ -31,6 +31,15 @@ module.hot.accept([

window.___emitter = emitter

if (
process.env.GATSBY_EXPERIMENTAL_CONCURRENT_FEATURES &&
!ReactDOM.unstable_createRoot
) {
throw new Error(
`The GATSBY_EXPERIMENTAL_CONCURRENT_FEATURES flag is not compatible with your React version. Please install "react@0.0.0-experimental-57768ef90" and "react-dom@0.0.0-experimental-57768ef90" or higher.`
)
}

const loader = new DevLoader(asyncRequires, matchPaths)
setLoader(loader)
loader.setApiRunner(apiRunner)
Expand Down Expand Up @@ -127,12 +136,25 @@ apiRunnerAsync(`onClientEntry`).then(() => {
const rootElement = document.getElementById(`___gatsby`)

const focusEl = document.getElementById(`gatsby-focus-wrapper`)

// Client only pages have any empty body so we just do a normal
// render to avoid React complaining about hydration mis-matches.
let defaultRenderer = ReactDOM.render
if (focusEl && focusEl.children.length) {
if (
process.env.GATSBY_EXPERIMENTAL_CONCURRENT_FEATURES &&
ReactDOM.unstable_createRoot
) {
defaultRenderer = ReactDOM.unstable_createRoot
} else {
defaultRenderer = ReactDOM.hydrate
}
}

const renderer = apiRunner(
`replaceHydrateFunction`,
undefined,
// Client only pages have any empty body so we just do a normal
// render to avoid React complaining about hydration mis-matches.
focusEl && focusEl.children.length > 0 ? ReactDOM.hydrate : ReactDOM.render
defaultRenderer
)[0]

let dismissLoadingIndicator
Expand Down Expand Up @@ -166,31 +188,57 @@ apiRunnerAsync(`onClientEntry`).then(() => {
]).then(() => {
navigationInit()

function onHydrated() {
apiRunner(`onInitialClientRender`)

// Render query on demand overlay
if (
process.env.GATSBY_QUERY_ON_DEMAND_LOADING_INDICATOR &&
process.env.GATSBY_QUERY_ON_DEMAND_LOADING_INDICATOR === `true`
) {
const indicatorMountElement = document.createElement(`div`)
indicatorMountElement.setAttribute(
`id`,
`query-on-demand-indicator-element`
)
document.body.append(indicatorMountElement)

if (renderer === ReactDOM.unstable_createRoot) {
renderer(indicatorMountElement).render(
<LoadingIndicatorEventHandler />
)
} else {
renderer(<LoadingIndicatorEventHandler />, indicatorMountElement)
}
}
}

function App() {
const onClientEntryRanRef = React.useRef(false)

React.useEffect(() => {
if (!onClientEntryRanRef.current) {
onClientEntryRanRef.current = true

onHydrated()
}
}, [])

return <Root />
}

domReady(() => {
if (dismissLoadingIndicator) {
dismissLoadingIndicator()
}

renderer(<Root />, rootElement, () => {
apiRunner(`onInitialClientRender`)

// Render query on demand overlay
if (
process.env.GATSBY_QUERY_ON_DEMAND_LOADING_INDICATOR &&
process.env.GATSBY_QUERY_ON_DEMAND_LOADING_INDICATOR === `true`
) {
const indicatorMountElement = document.createElement(`div`)
indicatorMountElement.setAttribute(
`id`,
`query-on-demand-indicator-element`
)
document.body.append(indicatorMountElement)
ReactDOM.render(
<LoadingIndicatorEventHandler />,
indicatorMountElement
)
}
})
if (renderer === ReactDOM.unstable_createRoot) {
renderer(rootElement, {
hydrate: true,
}).render(<App />)
} else {
renderer(<App />, rootElement)
}
})
})
})
36 changes: 27 additions & 9 deletions packages/gatsby/cache-dir/production-app.js
Expand Up @@ -175,24 +175,42 @@ apiRunnerAsync(`onClientEntry`).then(() => {
}
).pop()

const App = () => <GatsbyRoot>{SiteRoot}</GatsbyRoot>
const App = function App() {
const onClientEntryRanRef = React.useRef(false)

React.useEffect(() => {
if (!onClientEntryRanRef.current) {
onClientEntryRanRef.current = true
performance.mark(`onInitialClientRender`)

apiRunner(`onInitialClientRender`)
}
}, [])

return <GatsbyRoot>{SiteRoot}</GatsbyRoot>
}

const renderer = apiRunner(
`replaceHydrateFunction`,
undefined,
ReactDOM.hydrate
process.env.GATSBY_EXPERIMENTAL_CONCURRENT_FEATURES
? ReactDOM.unstable_createRoot
: ReactDOM.hydrate
)[0]

domReady(() => {
renderer(
<App />,
const container =
typeof window !== `undefined`
? document.getElementById(`___gatsby`)
: void 0,
() => {
apiRunner(`onInitialClientRender`)
}
)
: null

if (renderer === ReactDOM.unstable_createRoot) {
renderer(container, {
hydrate: true,
}).render(<App />)
} else {
renderer(<App />, container)
}
})
})
})
24 changes: 24 additions & 0 deletions packages/gatsby/src/utils/flags.ts
Expand Up @@ -176,6 +176,30 @@ const activeFlags: Array<IFlag> = [
umbrellaIssue: `https://gatsby.dev/functions-feedback`,
testFitness: (): fitnessEnum => true,
},
{
name: `CONCURRENT_FEATURES`,
env: `GATSBY_EXPERIMENTAL_CONCURRENT_FEATURES`,
command: `all`,
telemetryId: `ConcurrentFeatures`,
experimental: true,
description: `Enable React's concurrent features`,
// umbrellaIssue: `https://gatsby.dev/concurrent-features`,
testFitness: (): fitnessEnum => {
// Because of this, this flag will never show up
const semverConstraints = {
react: `^0.0.0-experimental-57768ef90`,
"react-dom": `^0.0.0-experimental-57768ef90`,
}

if (satisfiesSemvers(semverConstraints)) {
return true
} else {
// react & react-dom is either not installed or not new enough so
// just disable — it won't work anyways.
return false
}
},
},
]

export default activeFlags

0 comments on commit d543723

Please sign in to comment.