Skip to content

Instantly share code, notes, and snippets.

@ninetails
Created February 28, 2019 01:29
Show Gist options
  • Save ninetails/414e63f3207f4f6b18bc93e885c9a869 to your computer and use it in GitHub Desktop.
Save ninetails/414e63f3207f4f6b18bc93e885c9a869 to your computer and use it in GitHub Desktop.
reset
import React, { Fragment, Suspense } from 'react'
import Head from '@ninetails-monorepo-react-ssr/react-kabocha'
import { Link, Route, Switch } from 'react-router-dom'
const UniversalSuspense = global.window ? Suspense : Fragment
const test = {
read (timeout, cache = global.window) {
if (this.value) {
if (cache) {
return this.value
}
const output = this.value
delete this.value
delete this.promise
return output
}
if (this.promise) {
throw this.promise
}
this.promise = new Promise(resolve => {
console.log('settimeout start', Date.now() / 1000)
setTimeout(() => {
console.log('settimeout end', Date.now() / 1000)
this.value = 'foo'
resolve(this.value)
}, timeout)
})
throw this.promise
}
}
function LazyTest () {
const testValue = test.read(2000)
return <div>{testValue}</div>
}
function Home () {
return (
<div>
<Head>
<title>Home</title>
</Head>
Home
</div>
)
}
function About () {
return (
<div>
<Head>
<title>About</title>
</Head>
About
<UniversalSuspense maxDuration={500} fallback={<div>loading...</div>}>
<LazyTest />
</UniversalSuspense>
</div>
)
}
const App = () => (
<div>
<Head>
<meta charSet='utf-8' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
</Head>
<nav>
<ul>
<li>
<Link to='/'>Home</Link>
</li>
<li>
<Link to='/about'>About</Link>
</li>
</ul>
</nav>
<Switch>
<Route exact path='/' component={Home} />
<Route path='/about' component={About} />
</Switch>
</div>
)
export default App
import React from 'react'
import ReactDOM from 'react-dom'
import { HeadProvider } from '@ninetails-monorepo-react-ssr/react-kabocha'
import { BrowserRouter as Router } from 'react-router-dom'
import getRoot from './client/getRoot'
import App from './App'
const root = getRoot(process.env.REACT_APP_ROOT)
ReactDOM.createRoot(root, { hydrate: root.hasChildNodes() }).render(
<HeadProvider>
<Router>
<App />
</Router>
</HeadProvider>
)
import React from 'react'
import { renderToStaticNodeStream, renderToString } from 'react-dom/server'
import {
HeadProvider,
createRegistry
} from '@ninetails-monorepo-react-ssr/react-kabocha'
import { StaticRouter as Router } from 'react-router-dom'
import App from './App'
async function renderContent (props) {
try {
return renderToString(
<HeadProvider registry={props.registry}>
<Router location={props.location} context={props.context}>
<App />
</Router>
</HeadProvider>
)
} catch (err) {
if (err instanceof Promise) {
await err
return renderContent(props)
}
throw err
}
}
function serverRenderer ({ clientStats, serverStats }) {
const { main } = clientStats.assetsByChunkName
const mainSrc = typeof main === 'string' ? main : main[0]
return async (req, res, next) => {
const registry = createRegistry()
const context = {}
try {
const content = await renderContent({
context,
location: req.url,
registry
})
if (context.url) {
return res.redirect(301, context.url)
}
res.status(200).write('<!doctype html>')
renderToStaticNodeStream(
<html>
<head>{registry.head()}</head>
<body>
<div
id={process.env.REACT_APP_ROOT || 'root'}
dangerouslySetInnerHTML={{ __html: content }}
/>
<script src={`/${mainSrc}`} />
</body>
</html>
).pipe(
res,
{ end: 'false' }
)
} catch (err) {
// @todo error page
console.error(err)
res.status(500).send('Server error')
}
}
}
export default serverRenderer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment