Reputation: 525
I'm working on moving my react v0.14
+ redux v3.0
+ react-router v1.0
codebase from client-side rendering to server-side rendering using webpack v1.12
to bundle and code-split into chunks to load routes and components on-demand.
Im following and basing my setup on https://github.com/rackt/example-react-router-server-rendering-lazy-routes as I think it provides simplicity and great utilities. All day yesterday I have been working on moving to server-side rendering but I run into a few issues I haven't been able to solve and I'm not completely sure if they are because of webpack
not being setup correctly, if am doing something wrong with react-router
on the server/client or the routes config, or if its something I'm doing wrong with setting up redux
that is causing these issues.
I run into the following issues:
text/javascript
so the styles don't show up unless they are inline.var fs = require('fs')
var path = require('path')
var webpack = require('webpack')
module.exports = {
devtool: 'source-map',
entry: './client/client.jsx',
output: {
path: __dirname + '/__build__',
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: '/__build__/'
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader'
}
]
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
compressor: { warnings: false },
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
})
]
}
import http from 'http';
import React from 'react';
import {renderToString} from 'react-dom/server';
import { match, RoutingContext } from 'react-router';
import {Provider} from 'react-redux';
import configureStore from './../common/store/store.js';
import fs from 'fs';
import { createPage, write, writeError, writeNotFound, redirect } from './server-utils.js';
import routes from './../common/routes/rootRoutes.js';
const PORT = process.env.PORT || 3000;
var store = configureStore();
const initialState = store.getState();
function renderApp(props, res) {
var markup = renderToString(
<Provider store={store}>
<RoutingContext {...props}/>
</Provider>
);
var html = createPage(markup, initialState);
write(html, 'text/html', res);
}
http.createServer((req, res) => {
if (req.url === '/favicon.ico') {
write('haha', 'text/plain', res);
}
// serve JavaScript assets
else if (/__build__/.test(req.url)) {
fs.readFile(`.${req.url}`, (err, data) => {
write(data, 'text/javascript', res);
})
}
// handle all other urls with React Router
else {
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
if (error)
writeError('ERROR!', res);
else if (redirectLocation)
redirect(redirectLocation, res);
else if (renderProps)
renderApp(renderProps, res);
else
writeNotFound(res);
});
}
}).listen(PORT)
console.log(`listening on port ${PORT}`)
Is the same as from the repo that I posted above example-react-router-server-rendering-lazy-routes
just navigate to /modules/utils/server-utils.js
in the repo.The only difference is the createPage
function:
export function createPage(html, initialState) {
return( `
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="./../bower_components/Ionicons/css/ionicons.min.css">
<link rel="stylesheet" href="./../dist/main.css">
<title>Sell Your Soles</title>
</head>
<body>
<div id="app">${html}</div>
<script>window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};</script>
<script src="/__build__/main.js"></script>
</body>
</html>
`);
}
// polyfill webpack require.ensure
if (typeof require.ensure !== 'function') require.ensure = (d, c) => c(require)
import App from '../components/App.jsx'
import Landing from '../components/Landing/Landing.jsx'
export default {
path: '/',
component: App,
getChildRoutes(location, cb) {
require.ensure([], (require) => {
cb(null, [
require('./UserProfile/UserProfileRoute.js'),
require('./UserHome/UserHomeRoute.js'),
require('./SneakerPage/SneakerPageRoute.js'),
require('./Reviews/ReviewsRoute.js'),
require('./Listings/ListingsRoute.js'),
require('./Events/EventsRoute.js')
])
})
},
indexRoute: {
component: Landing
}
}
import UserProfile from '../../components/UserProfile/UserProfile.jsx';
export default {
path: 'profile',
component: UserProfile
}
import React from 'react';
import { match, Router } from 'react-router';
import { render } from 'react-dom';
import { createHistory } from 'history';
import routes from './../common/routes/rootRoutes.js';
import {Provider} from 'react-redux';
import configureStore from './../common/store/store.js';
const { pathname, search, hash } = window.location;
const location = `${pathname}${search}${hash}`;
const initialState = window.__INITIAL_STATE__;
const store = configureStore(initialState);
// calling `match` is simply for side effects of
// loading route/component code for the initial location
match({ routes, location }, () => {
render(
<Provider store={store}>
<Router routes={routes} history={createHistory()} />
</Provider>,
document.getElementById('app')
);
});
Upvotes: 7
Views: 9670
Reputation: 615
I helped you out on discord, but I thought I'd post the answer here as well.
If you are using babel6 (instead of babel5) and using export default in your components, then you need to update your routes to the following:
getChildRoutes(location, cb) {
require.ensure([], (require) => {
cb(null, [
require('./UserProfile/UserProfileRoute.js').default,
require('./UserHome/UserHomeRoute.js').default,
require('./SneakerPage/SneakerPageRoute.js').default,
require('./Reviews/ReviewsRoute.js').default,
require('./Listings/ListingsRoute.js').default,
require('./Events/EventsRoute.js').default
])
})
}
See this SO discussion for more details: Babel 6 changes how it exports default
Upvotes: 11
Reputation: 7382
If you happen to upgrade to Webpack 2 (+ tree shaking), you would use System.import
instead of requires, which is incredibly useful.
Here's how:
import App from 'containers/App';
function errorLoading(err) {
console.error('Dynamic page loading failed', err);
}
function loadRoute(cb) {
return (module) => cb(null, module.default);
}
export default {
component: App,
childRoutes: [
{
path: '/',
getComponent(location, cb) {
System.import('pages/Home')
.then(loadRoute(cb))
.catch(errorLoading);
}
},
{
path: 'blog',
getComponent(location, cb) {
System.import('pages/Blog')
.then(loadRoute(cb))
.catch(errorLoading);
}
}
]
};
You can get the entire guide in this blog post: Automatic Code Splitting for React Router
Upvotes: 3