-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathhandler.js
136 lines (111 loc) · 3.94 KB
/
handler.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import React from 'react';
import { Provider } from 'react-redux';
import { StaticRouter, matchPath } from 'react-router';
import { setMobileDetect, mobileParser } from 'react-responsive-redux';
import { renderToString } from 'react-dom/server';
import { getBundles } from 'react-loadable/webpack';
import Loadable from 'react-loadable';
import render from './render';
import routes from '@routes';
import configureStore from '@store';
import App from '@containers/App';
import config from '@config';
let stats = null;
// This is a small 'hack' to tell webpack to avoid resolving the below file
// during compilation, since react-loadable.json may or may not exist.
const requireFunc = typeof __webpack_require__ === 'function'
? __non_webpack_require__
: require;
if (config.enableDynamicImports) {
stats = requireFunc('../../react-loadable.json');
}
export default function handleRender(req, res) {
let context = {}, modules = [], initialState = {};
// Create a new Redux store instance
const store = configureStore(initialState);
// Server side responsive detection
const mobileDetect = mobileParser(req);
// set mobile detection for our responsive store
store.dispatch(setMobileDetect(mobileDetect));
// Grab the initial state from our Redux store
const finalState = store.getState();
// If SSR is disabled, just render the skeleton HTML.
if (!config.enableSSR) {
const markup = render(null, finalState, []);
return res.send(markup);
}
// See react-router's Server Rendering section:
// https://reacttraining.com/react-router/web/guides/server-rendering
const matchRoutes = routes => {
return routes.reduce((matches, route) => {
const { path } = route;
const match = matchPath(req.baseUrl, {
path,
exact: true,
strict: false
});
if (match) {
const wc =
(route.component && route.component.WrappedComponent) ||
route.component;
matches.push({
route,
match,
fetchData: (wc && wc.fetchData) || (() => Promise.resolve())
});
}
if (!match && route.routes) {
// recursively try to match nested routes
const nested = matchRoutes(route.routes);
if (nested.length) {
matches = matches.concat(nested);
}
}
return matches;
}, []);
};
const matches = matchRoutes(routes);
// No matched route, send an error.
if (!matches.length) {
return res.status(500).send('Server Error');
}
const getComponent = () => {
let component = (
<Provider store={store}>
<StaticRouter context={context} location={req.baseUrl}>
<App />
</StaticRouter>
</Provider>
);
if (config.enableDynamicImports) {
return (
<Loadable.Capture report={moduleName => modules.push(moduleName)}>
{component}
</Loadable.Capture>
);
}
return component;
};
// There's a match, render the component with the matched route, firing off
// any fetchData methods that are statically defined on the server.
const fetchData = matches.map(match => {
const { fetchData, ...rest } = match; // eslint-disable-line no-unused-vars
// return fetch data Promise, excluding unnecessary fetchData method
return match.fetchData({ store, ...rest });
});
// Execute the render only after all promises have been resolved.
Promise.all(fetchData).then(() => {
const state = store.getState();
const html = renderToString(getComponent());
const bundles = stats && getBundles(stats, modules) || [];
const markup = render(html, state, bundles);
const status = matches.length && matches[0].match.path === '*' ? 404 : 200;
// A 301 redirect was rendered somewhere if context.url exists after
// rendering has happened.
if (context.url) {
return res.redirect(302, context.url);
}
res.contentType('text/html');
return res.status(status).send(markup);
});
}