react-webpack-node
Version:
A simple Node.js app using Express 4 with Webpack, React, alt, ImmutableJS
103 lines (97 loc) • 3.71 kB
JSX
import axios from 'axios';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { createMemoryHistory, match, RouterContext } from 'react-router';
import { Provider } from 'react-redux';
import createRoutes from 'routes';
import configureStore from 'store/configureStore';
import preRenderMiddleware from 'middlewares/preRenderMiddleware';
import header from 'components/Meta';
const clientConfig = {
host: process.env.HOSTNAME || 'localhost',
port: process.env.PORT || '3000'
};
// configure baseURL for axios requests (for serverside API calls)
axios.defaults.baseURL = `http://${clientConfig.host}:${clientConfig.port}`;
/*
* Export render function to be used in server/config/routes.js
* We grab the state passed in from the server and the req object from Express/Koa
* and pass it into the Router.run function.
*/
export default function render(req, res) {
const authenticated = req.isAuthenticated();
const history = createMemoryHistory();
const store = configureStore({
user: {
authenticated,
isWaiting: false,
message: '',
isLogin: true
}
}, history);
const routes = createRoutes(store);
/*
* From the react-router docs:
*
* This function is to be used for server-side rendering. It matches a set of routes to
* a location, without rendering, and calls a callback(err, redirect, props)
* when it's done.
*
* The function will create a `history` for you, passing additional `options` to create it.
* These options can include `basename` to control the base name for URLs, as well as the pair
* of `parseQueryString` and `stringifyQuery` to control query string parsing and serializing.
* You can also pass in an already instantiated `history` object, which can be constructured
* however you like.
*
* The three arguments to the callback function you pass to `match` are:
* - err: A javascript Error object if an error occured, `undefined` otherwise.
* - redirect: A `Location` object if the route is a redirect, `undefined` otherwise
* - props: The props you should pass to the routing context if the route matched,
* `undefined` otherwise.
* If all three parameters are `undefined`, this means that there was no route found matching the
* given location.
*/
match({routes, location: req.url}, (err, redirect, props) => {
if (err) {
res.status(500).json(err);
} else if (redirect) {
res.redirect(302, redirect.pathname + redirect.search);
} else if (props) {
// This method waits for all render component
// promises to resolve before returning to browser
preRenderMiddleware(
store.dispatch,
props.components,
props.params
)
.then(() => {
const initialState = store.getState();
const componentHTML = renderToString(
<Provider store={store}>
<RouterContext {...props} />
</Provider>
);
res.status(200).send(`
<!doctype html>
<html ${header.htmlAttributes.toString()}>
<head>
${header.title.toString()}
${header.meta.toString()}
${header.link.toString()}
</head>
<body>
<div id="app">${componentHTML}</div>
<script>window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};</script>
<script type="text/javascript" charset="utf-8" src="/assets/app.js"></script>
</body>
</html>
`);
})
.catch((err) => {
res.status(500).json(err);
});
} else {
res.sendStatus(404);
}
});
}