Skip to content

Commit

Permalink
fix: adjusted client side hydration
Browse files Browse the repository at this point in the history
  • Loading branch information
BowlingX committed Feb 28, 2024
1 parent 8404dd7 commit 0efcd9f
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 73 deletions.
117 changes: 69 additions & 48 deletions src/shell/app-shell.client.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,82 @@
import { loadableReady } from '@loadable/component'
import ReactDOM from 'react-dom'
import React from 'react'
import { ApplicationRoot } from './components/ApplicationRoot.js'
import application from '@main'
import { EV_AFTER_HYDRATION, EV_BEFORE_HYDRATION } from '../server/events.js'
import { NextStaticData } from '../types/entrypoint.js'

async function init() {
if (typeof window.__NEXT_STATIC_DATA__ === 'undefined') {
throw new Error(
'[next-static]: Client side rendering expected `__NEXT_STATIC_DATA__` to be defined.'
)
declare global {
interface Window {
/* prod */
__NEXT_STATIC_CONTEXT_EXTEND__: Pick<NextStaticData, 'context'>
}
const {
locale,
locales,
basePath,
domains,
defaultLocale,
linkPrefix,
context,
query,
} = window.__NEXT_STATIC_DATA__
window.dispatchEvent(new CustomEvent(EV_BEFORE_HYDRATION))

await loadableReady()
const { components, props } = await application(context)

for (const [index, Component] of components.entries()) {
const selector = `[data-next-static-index="${index}"]`
const root = document.querySelector(selector)
if (!root) {
throw new Error(
`[next-static] Unable to rehydrate static root. Cannot find selector ${selector}.`
)
}
ReactDOM.hydrate(
<ApplicationRoot
locale={locale}
domains={domains}
defaultLocale={defaultLocale}
locales={locales}
basePath={basePath}
linkPrefix={linkPrefix}
query={query}
>
<Component {...props} />
</ApplicationRoot>,
root
)
}
window.dispatchEvent(new CustomEvent(EV_AFTER_HYDRATION))
}

;(async () => {
async function init() {
try {
await init()
const initialData = JSON.parse(
document.getElementById('__NEXT_STATIC_DATA__')!.textContent!
) as NextStaticData

const thisInitialData = (initialData || {}) as NextStaticData

const config = {
...thisInitialData,
context: {
...thisInitialData.context,
...(window.__NEXT_STATIC_CONTEXT_EXTEND__ || {}),
},
}

const {
locale,
locales,
basePath,
domains,
defaultLocale,
linkPrefix,
context,
query,
} = config

window.dispatchEvent(new CustomEvent(EV_BEFORE_HYDRATION))

const { ApplicationRoot } = await import('./components/ApplicationRoot.js')
const application = await import('@main')
await loadableReady()
const { components, props } = await application.default(context)

for (const [index, Component] of components.entries()) {
const selector = `[data-next-static-index="${index}"]`
const root = document.querySelector(selector)
if (!root) {
throw new Error(
`[next-static] Unable to rehydrate static root. Cannot find selector ${selector}.`
)
}
ReactDOM.hydrate(
<ApplicationRoot
locale={locale}
domains={domains}
defaultLocale={defaultLocale}
locales={locales}
basePath={basePath}
linkPrefix={linkPrefix}
query={query}
>
<Component {...props} />
</ApplicationRoot>,
root
)
}
window.dispatchEvent(new CustomEvent(EV_AFTER_HYDRATION))
} catch (e) {
console.error(`[next-static] Error during application init.`, e)
}
}

;(() => {
if (document.readyState === 'complete') {
return init()
}
document.addEventListener('DOMContentLoaded', init)
})()
15 changes: 14 additions & 1 deletion src/shell/app-shell.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,26 @@ export default async function (

const renderedApp = renderToStaticMarkup(Application)

const { runtimeConfig, publicAssetPath, query, ...restConfig } =
NEXT_STATIC_DATA
const Scripts = () => (
<>
<script
id="__NEXT_STATIC_INFO__"
type="application/json"
dangerouslySetInnerHTML={{
__html: JSON.stringify({
runtimeConfig,
publicAssetPath,
query,
}),
}}
/>
{chunkExtractor.getScriptElements()}
<script
id="__NEXT_STATIC_DATA__"
type="application/json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(NEXT_STATIC_DATA) }}
dangerouslySetInnerHTML={{ __html: JSON.stringify(restConfig) }}
/>
</>
)
Expand Down
30 changes: 6 additions & 24 deletions src/shell/init.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,23 @@ import { setConfig } from 'next/config.js'

declare let __webpack_public_path__: string

declare global {
interface Window {
/* prod */
__NEXT_STATIC_DATA__: NextStaticData
__NEXT_STATIC_CONTEXT_EXTEND__: Pick<NextStaticData, 'context'>
}
}

const initialData = JSON.parse(
document.getElementById('__NEXT_STATIC_DATA__')!.textContent!
) as NextStaticData

const thisInitialData = (initialData || {}) as NextStaticData

window.__NEXT_STATIC_DATA__ = {
...thisInitialData,
context: {
...thisInitialData.context,
...(window.__NEXT_STATIC_CONTEXT_EXTEND__ || {}),
},
}
const info = JSON.parse(
document.getElementById('__NEXT_STATIC_INFO__')!.textContent!
) as Pick<NextStaticData, 'publicAssetPath' | 'runtimeConfig' | 'query'>

window.__NEXT_DATA__ = {
props: { pageProps: {} },
page: '',
query: thisInitialData.query || {},
query: info.query || {},
buildId: '',
}

// Initialize next/config with the environment configuration
setConfig({
serverRuntimeConfig: {},
publicRuntimeConfig: thisInitialData.runtimeConfig || {},
publicRuntimeConfig: info.runtimeConfig || {},
})

// we have to make sure that any additional async requests are
// resolved through our public asset path (which can also be a different domain)
__webpack_public_path__ = thisInitialData.publicAssetPath
__webpack_public_path__ = info.publicAssetPath

0 comments on commit 0efcd9f

Please sign in to comment.