ファイル
btrc-hub/frontend/src/main.tsx
T
2026-10-18 22:14:40 +09:00

115 行
3.3 KiB
TypeScript

import { QueryClient, QueryClientProvider, hydrate } from '@tanstack/react-query'
import { createRoot, hydrateRoot } from 'react-dom/client'
import { HelmetProvider } from 'react-helmet-async'
import { I18nextProvider } from 'react-i18next'
import '@/index.css'
import App from '@/App'
import { createUII18n } from '@/lib/i18n'
import { prepareHelpPage } from '@/lib/help'
import { currentViewingLocale } from '@/lib/locales'
import { localesKeys } from '@/lib/queryKeys'
import { applyClientAnimationMode, getClientAnimationMode } from '@/lib/settings'
import type { DehydratedState } from '@tanstack/react-query'
const helmetContext = { }
const client = new QueryClient ({
defaultOptions: {
queries: { staleTime: 5 * 60 * 1000,
gcTime: 30 * 60 * 1000,
retry: 1 } } })
const mount = (serverRendered: boolean) => {
const instance = createUII18n (currentViewingLocale ())
const app = (
<HelmetProvider context={helmetContext}>
<QueryClientProvider client={client}>
<I18nextProvider i18n={instance}>
<App/>
</I18nextProvider>
</QueryClientProvider>
</HelmetProvider>)
const root = document.getElementById ('root')!
if (serverRendered)
hydrateRoot (root, app)
else
createRoot (root).render (app)
}
const start = () => {
applyClientAnimationMode (getClientAnimationMode ())
const bootstrap = document.getElementById ('ssr-state')
if (bootstrap == null)
{
mount (false)
return
}
const storageKey = 'btrc_hub.ssr-recovery'
const identity = `${import.meta.url}:${location.pathname}${location.search}`
let pending = false
let mounted = false
const retry = () => { void attempt () }
const attempt = async () => {
if (pending || mounted)
return
pending = true
try
{
const data = JSON.parse (bootstrap.textContent ?? '') as {
state: DehydratedState; status: number }
if (data?.state == null || !(Array.isArray (data.state.queries)))
throw new Error ('Invalid SSR query state')
hydrate (client, data.state)
if (client.getQueryData (localesKeys.index) == null)
throw new Error ('Missing SSR locales')
const status = await prepareHelpPage (client, new URL (window.location.href))
if (status !== data.status)
throw new Error ('Server and browser Help versions differ')
mount (true)
mounted = true
for (const event of ['online', 'focus', 'pageshow'])
window.removeEventListener (event, retry)
try
{
sessionStorage.removeItem (storageKey)
}
catch
{
// Storage may be unavailable; successful hydration needs no recovery marker.
}
}
catch (error)
{
console.error ('[SSR] Preparation failed; keeping server HTML', error)
// A new document also clears failed dynamic-import module caches.
// Never clear the root or attempt CSR over unmatched server markup.
try
{
if (navigator.onLine && sessionStorage.getItem (storageKey) !== identity)
{
sessionStorage.setItem (storageKey, identity)
window.location.reload ()
}
}
catch
{
// Keep the readable document and retry on online/focus/pageshow instead.
}
}
finally
{
pending = false
}
}
for (const event of ['online', 'focus', 'pageshow'])
window.addEventListener (event, retry)
retry ()
}
start ()