Last active
July 19, 2017 03:37
-
-
Save coopermaruyama/1299eab5cfd18acdcc2c424ec8988b86 to your computer and use it in GitHub Desktop.
Rendering
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Dynamic content is served via this middleware. Each page has a 'path' | |
* attribute which determines the path on which it should render. Any time a | |
* request is made to the server, we check the requested path to see if any | |
* page has that same path, in which case we step in and render that page. | |
* @flow | |
*/ | |
/* eslint-disable react/no-danger, no-inline-comments */ | |
import type { $Request, $Response } from 'express'; | |
import React from 'react'; | |
import { SheetsRegistryProvider, SheetsRegistry } from 'react-jss'; | |
import Page from '~/models/page'; | |
import ReactDOMServer from 'react-dom/server'; | |
import Templates from 'shared/templates'; | |
import Slot from 'shared/components/Slot'; | |
/** | |
* The primary middleware. Essentially checks the path of a request and if it | |
* matches a CMS page, renders it. | |
*/ | |
const cms = (req: $Request, res: $Response, next: Function) => { | |
Page.findOne({ | |
path: req.path | |
}).exec().then((page) => { | |
if (page) { | |
const html = render(page._doc); | |
return res.send(html); | |
} | |
return next(); | |
}); | |
}; | |
/** | |
* Handles the rendering of the page. Implements React server-side rendering, | |
* and injects script tags for the template and widgets it will need. | |
*/ | |
function render(page) { | |
// On the server, react-jss needs this as a target to push styles to. | |
const sheets = new SheetsRegistry(); | |
// Get the paths to the widgets which the page depends on. | |
const widgetPaths = getRequiredWidgetPaths(page); | |
// templateId matches the right directory in shared/templates/<templateId> | |
const templateId = page.template.id; | |
// `Templates` contains all the templates, get the one we need to render. | |
const Template = Templates[templateId]; | |
const props = { | |
slots: page.slots, | |
renderer: Slot | |
}; | |
// This will give us the server-side rendered HTML for our page. | |
const html = ReactDOMServer.renderToString( | |
<SheetsRegistryProvider registry={sheets}> | |
<Template {...props} /> | |
</SheetsRegistryProvider> | |
); | |
return ReactDOMServer.renderToString( | |
<html lang="en-US" style={{ margin: 0 }}> | |
<head> | |
<link href="/styles.css" type="text/css" rel="stylesheet" /> | |
{/* Inject the stylesheet from react-jss */} | |
<style type="text/css"> | |
{sheets.toString()} | |
</style> | |
</head> | |
<body style={{ margin: 0 }}> | |
{/* Our server-side rendered HTML */} | |
<div | |
id="content" | |
dangerouslySetInnerHTML={{ __html: html }} | |
/> | |
{/* Load React from CDN for speed and so we dont need to bundle it */} | |
<script | |
src="//cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react.min.js" | |
/> | |
<script | |
src="//cdnjs.cloudflare.com/ajax/libs/react/15.5.4/react-dom.min.js" | |
/> | |
{/* Load each widget this page depends on */} | |
{widgetPaths.map(path => <script src={path} key={path} />)} | |
{/* And the template, too */} | |
<script src={`/dist/templates/${templateId}.js`} /> | |
{/* And finally, initalize React on client-side */} | |
<script | |
dangerouslySetInnerHTML={{ | |
__html: ` | |
var props = ${JSON.stringify(props)}; | |
ReactDOM.render( | |
React.createElement(${templateId}, props), | |
document.getElementById('content') | |
); | |
` | |
}} | |
/> | |
</body> | |
</html> | |
); | |
} | |
/** | |
* Parses every widget used by a page and returns a unique list of paths to | |
* each widget used by the page. | |
*/ | |
function getRequiredWidgetPaths(page) { | |
const requiredWidgets = []; | |
page.slots.forEach((slot) => { | |
slot.components.forEach((component) => { | |
if (requiredWidgets.indexOf(component.componentId) === -1) { | |
requiredWidgets.push(component.componentId); | |
} | |
}); | |
}); | |
return requiredWidgets.map(w => `/dist/widgets/${w}.js`); | |
} | |
function getDefaultStyles() { | |
return { | |
'html, body': { | |
margin: 0 | |
} | |
}; | |
} | |
export default cms; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment