Repository URL to install this package:
|
Version:
1.2.7 ▾
|
import React from 'react'
import { renderToString } from 'react-dom/server'
import { ServerStyleSheet } from 'view-container/styles/styled-components'
import { StaticRouter } from 'react-router-dom'
import Helmet from 'react-helmet'
import serialize from 'serialize-javascript'
import { getDataFromTree, ApolloProvider } from 'react-apollo'
import { useStaticRendering, Provider } from 'xmobx/mobx-react'
import { requireClient } from './requireClient'
import { respondRedirect, respondHyperText, respondError } from '../responders'
import { cacheIfNeeded } from '../../middleware/cacheMiddleware'
import { ServerHTML } from '../HTML'
import { Request, Response, ReactRouterContext } from '../typings'
/**
* @see https://github.com/mobxjs/mobx-react
* > "To avoid leaking memory when server side rendering"
*/
useStaticRendering(true)
function renderFinalString(html: JSX.Element) {
// @note this was renderToStaticMarkup
const appString = renderToString(html)
if (process.env.IS_SSR_TEST) {
const stringScript = `<script>window.__SSR_STRING__=${serialize(
appString
)}</script>`
const output = appString + stringScript
return output
} else {
return appString
}
}
export async function renderUsingServerSideRendering(
req: Request,
res: Response,
nonce: string
) {
/**
* would scope these higher, but req is scoped
* @todo probably should have a class
*/
const onError = apolloRenderingError => {
console.error('[EXCEPTION_ALERT] [ssr] APOLLO ERROR', apolloRenderingError)
respondError(res, apolloRenderingError)
}
console.log('[SSR] RENDERING_SSR_FOR_ROUTE:')
/**
* injected client dependencies
* this is set in development poc & dist/index
*/
const { App, client, OmniStore } = requireClient()
/**
* Create a context for <StaticRouter>,
* which will allow us to query for the results of the render.
*/
const reactRouterContext: ReactRouterContext = {}
/**
* Create the job context for our provider,
* this grants us the ability to track the resolved jobs
* so we can send it back to the client.
*/
const store = OmniStore.create()
/**
* @description Declare our React application.
* @see https://www.apollographql.com/docs/react/recipes/server-side-rendering.html
*/
const appView = (
<StaticRouter location={req.url} context={reactRouterContext}>
<ApolloProvider client={client}>
<Provider store={store}>
<App />
</Provider>
</ApolloProvider>
</StaticRouter>
)
function renderReactToString(initialState: Object) {
const sheet = new ServerStyleSheet()
const styledView = sheet.collectStyles(appView)
const appString = renderToString(styledView)
const styledTags = sheet.getStyleElement()
/**
* @see https://github.com/styled-components/styled-components/issues/378
* @todo https://github.com/nfl/react-helmet/issues/216
*/
const htmlView = (
<ServerHTML
isCached={req.SHOULD_CACHE_SSR}
reactAppString={appString}
nonce={nonce}
helmet={Helmet.rewind()}
styledTags={styledTags}
apolloState={initialState}
storeState={store}
routerState={reactRouterContext}
/>
)
return renderFinalString(htmlView)
}
const onBootstrapped = () => {
/**
* @todo https://www.apollographql.com/docs/react/features/server-side-rendering.html#server-initialization
*/
const initialState = client.extract()
console.time('_render_app_to_string')
const html = renderReactToString(initialState)
console.timeEnd('_render_app_to_string')
cacheIfNeeded(req, res, html)
// @NOTE done here because it may be set in server html as a side effect
const { url, status } = reactRouterContext
/**
* Check if the router context contains a redirect, if so we need to set
* the specific status and redirect header and end the res.
*/
if (url) {
respondRedirect(res, url)
} else {
respondHyperText(res, status, html)
}
}
const encasedOnBootstrap = () => {
try {
onBootstrapped()
} catch (renderReactToStringException) {
console.log('[ssr] TOSTRING ERROR', renderReactToStringException)
respondError(res, renderReactToStringException)
}
}
try {
console.time('get_data_from_tree')
getDataFromTree(appView)
.then(encasedOnBootstrap)
.catch(onError)
console.timeEnd('get_data_from_tree')
} catch (bootstrapperException) {
console.log('[ssr] BOOTSTRAPPER ERROR', bootstrapperException)
respondError(res, bootstrapperException)
}
}