Pass preloaded state to client side.

This commit is contained in:
Vesa Luusua 2017-01-18 17:58:00 +02:00
parent 731db1991d
commit 1fa70eadf2
4 changed files with 33 additions and 11 deletions

View file

@ -5,6 +5,7 @@
<!--!title-->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--!preloadedStateScript-->
</head>
<body>
<div id="root"><!--!body--></div>

View file

@ -21,12 +21,13 @@ const helmet = require('helmet');
const compression = require('compression');
const path = require('path');
const fs = require('fs');
const auth = require('./auth');
const qs = require('qs');
const _ = require('lodash');
const React = require('react');
const { createServerRenderContext } = require('react-router');
const auth = require('./auth');
// Construct the bundle path where the server side renering function
// Construct the bundle path where the server side rendering function
// can be imported.
const buildPath = path.resolve(__dirname, '..', 'build');
const manifestPath = path.join(buildPath, 'asset-manifest.json');
@ -65,9 +66,17 @@ const template = _.template(indexHtml, {
escape: reNoMatch,
});
function render(url, context) {
const { head, body } = renderApp(url, context);
return template({ title: head.title.toString(), body });
function render(url, context, preloadedState) {
const { head, body } = renderApp(url, context, preloadedState);
// Preloaded state needs to be passed for client side too.
const preloadedStateScript = `
<script>
window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\x3c')};
</script>
`;
return template({ title: head.title.toString(), preloadedStateScript, body });
}
const env = process.env.NODE_ENV;
@ -91,7 +100,12 @@ app.use('/static', express.static(path.join(buildPath, 'static')));
app.get('*', (req, res) => {
const context = createServerRenderContext();
const html = render(req.url, context);
const filters = qs.parse(req.query);
// TODO fetch this asynchronously
const preloadedState = { search: { filters } };
const html = render(req.url, context, preloadedState);
const result = context.getResult();
if (result.redirect) {
@ -100,7 +114,7 @@ app.get('*', (req, res) => {
// Do a second render pass with the context to clue <Miss>
// components into rendering this time.
// See: https://react-router.now.sh/ServerRouter
res.status(404).send(render(req.url, context));
res.status(404).send(render(req.url, context, preloadedState));
} else {
res.send(html);
}

View file

@ -26,7 +26,7 @@ const { any, string } = PropTypes;
ClientApp.propTypes = { store: any.isRequired };
export const ServerApp = props => {
const { url, context } = props;
const { url, context, store } = props;
return (
<ServerRouter location={url} context={context}>
{
@ -52,8 +52,11 @@ ServerApp.propTypes = { url: string.isRequired, context: any.isRequired, store:
* - {String} body: Rendered application body of the given route
* - {Object} head: Application head metadata from react-helmet
*/
export const renderApp = (url, serverContext) => {
const body = ReactDOMServer.renderToString(<ServerApp url={url} context={serverContext} />);
export const renderApp = (url, serverContext, preloadedState) => {
const store = configureStore(preloadedState);
const body = ReactDOMServer.renderToString(
<ServerApp url={url} context={serverContext} store={store} />,
);
const head = Helmet.rewind();
return { head, body };
};

View file

@ -14,12 +14,16 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { ClientApp, renderApp } from './app';
import configureStore from './store';
import './index.css';
// If we're in a browser already, render the client application.
if (typeof window !== 'undefined') {
ReactDOM.render(<ClientApp />, document.getElementById('root'));
const preloadedState = window.__PRELOADED_STATE__ || {}; // eslint-disable-line no-underscore-dangle
const store = configureStore(preloadedState);
ReactDOM.render(<ClientApp store={store} />, document.getElementById('root'));
}
// Export the function for server side rendering.