From 7e503d36ddcaab0759cafba7dd69c49240bfce7e Mon Sep 17 00:00:00 2001 From: stevo Date: Thu, 1 Oct 2015 19:43:02 +0100 Subject: [PATCH] Add server rendering example Also update `react-transform-hmr` --- examples/.babelrc | 12 +-- examples/basic/index.js | 2 +- examples/basic/package.json | 2 +- examples/package.json | 6 +- examples/server-rendering/client.js | 42 ++++++++++ examples/server-rendering/components.js | 56 +++++++++++++ examples/server-rendering/constants.js | 1 + examples/server-rendering/package.json | 13 +++ examples/server-rendering/reducer.js | 6 ++ examples/server-rendering/routes.js | 12 +++ examples/server-rendering/server.js | 80 +++++++++++++++++++ .../webpack.config.clientDev.js | 27 +++++++ examples/webpack.config.base.js | 26 +++++- package.json | 1 + 14 files changed, 269 insertions(+), 17 deletions(-) create mode 100644 examples/server-rendering/client.js create mode 100644 examples/server-rendering/components.js create mode 100644 examples/server-rendering/constants.js create mode 100644 examples/server-rendering/package.json create mode 100644 examples/server-rendering/reducer.js create mode 100644 examples/server-rendering/routes.js create mode 100644 examples/server-rendering/server.js create mode 100644 examples/server-rendering/webpack.config.clientDev.js diff --git a/examples/.babelrc b/examples/.babelrc index b335dbf..b0b9a96 100644 --- a/examples/.babelrc +++ b/examples/.babelrc @@ -1,13 +1,3 @@ { - "stage": 0, - "plugins": [ - "react-transform" - ], - "extra": { - "react-transform": [{ - "target": "react-transform-webpack-hmr", - "imports": ["react"], - "locals": ["module"] - }] - } + "stage": 0 } diff --git a/examples/basic/index.js b/examples/basic/index.js index 3a43663..cc088a0 100644 --- a/examples/basic/index.js +++ b/examples/basic/index.js @@ -6,7 +6,7 @@ import { ReduxRouter, routerStateReducer, reduxReactRouter -} from 'redux-react-router'; +} from 'redux-router'; import { Route, Link } from 'react-router'; import { Provider, connect } from 'react-redux'; diff --git a/examples/basic/package.json b/examples/basic/package.json index 5bddd88..28a7646 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -1,5 +1,5 @@ { - "name": "redux-react-router-basic-example", + "name": "redux-router-basic-example", "version": "0.1.0", "description": "", "main": "server.js", diff --git a/examples/package.json b/examples/package.json index 50a2f96..9d4393c 100644 --- a/examples/package.json +++ b/examples/package.json @@ -1,5 +1,5 @@ { - "name": "redux-react-router-examples", + "name": "redux-router-examples", "version": "0.1.0", "description": "", "scripts": { @@ -15,8 +15,10 @@ "babel-runtime": "^5.8.20", "express": "^4.13.3", "lodash": "^3.10.1", - "react-transform-webpack-hmr": "^0.1.4", + "query-string": "^2.4.1", + "react-transform-hmr": "^1.0.1", "redux": "^2.0.0", + "serialize-javascript": "^1.1.2", "webpack": "^1.12.1", "webpack-dev-middleware": "^1.2.0", "webpack-hot-middleware": "^2.0.0" diff --git a/examples/server-rendering/client.js b/examples/server-rendering/client.js new file mode 100644 index 0000000..3065e4b --- /dev/null +++ b/examples/server-rendering/client.js @@ -0,0 +1,42 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { createStore, compose } from 'redux'; + +import { + ReduxRouter, + reduxReactRouter, +} from 'redux-router'; + +import { Provider } from 'react-redux'; +import { devTools } from 'redux-devtools'; +import { DevTools, DebugPanel, LogMonitor } from 'redux-devtools/lib/react'; +import createHistory from 'history/lib/createBrowserHistory'; + +import routes from './routes'; +import reducer from './reducer'; +import {MOUNT_ID} from './constants'; + +const store = compose( + reduxReactRouter({ createHistory }), + devTools() +)(createStore)(reducer, window.__initialState); + +const rootComponent = ( + + + +); + +const mountNode = document.getElementById(MOUNT_ID); + +// First render to match markup from server +ReactDOM.render(rootComponent, mountNode); +// Optional second render with dev-tools +ReactDOM.render(( +
+ {rootComponent} + + + +
+), mountNode); diff --git a/examples/server-rendering/components.js b/examples/server-rendering/components.js new file mode 100644 index 0000000..3873492 --- /dev/null +++ b/examples/server-rendering/components.js @@ -0,0 +1,56 @@ +import React, { Component, PropTypes } from 'react'; +import {connect} from 'react-redux'; +import {Link} from 'react-router'; + +@connect(state => ({ routerState: state.router })) +export const App = class App extends Component { + static propTypes = { + children: PropTypes.node + } + + render() { + const links = [ + '/', + '/parent?foo=bar', + '/parent/child?bar=baz', + '/parent/child/123?baz=foo' + ].map(l => +

+ {l} +

+ ); + + return ( +
+

App Container

+ {links} + {this.props.children} +
+ ); + } +}; + +export const Parent = class Parent extends Component { + static propTypes = { + children: PropTypes.node + } + + render() { + return ( +
+

Parent

+ {this.props.children} +
+ ); + } +}; + +export const Child = class Child extends Component { + render() { + return ( +
+

Child

+
+ ); + } +}; diff --git a/examples/server-rendering/constants.js b/examples/server-rendering/constants.js new file mode 100644 index 0000000..d4c0029 --- /dev/null +++ b/examples/server-rendering/constants.js @@ -0,0 +1 @@ +export const MOUNT_ID = 'root'; diff --git a/examples/server-rendering/package.json b/examples/server-rendering/package.json new file mode 100644 index 0000000..f4e28c3 --- /dev/null +++ b/examples/server-rendering/package.json @@ -0,0 +1,13 @@ +{ + "name": "redux-router-server-rendering-example", + "version": "0.1.0", + "description": "", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node -r babel/register server.js" + }, + "author": "Andrew Clark ", + "license": "MIT", + "dependencies": {} +} diff --git a/examples/server-rendering/reducer.js b/examples/server-rendering/reducer.js new file mode 100644 index 0000000..529d912 --- /dev/null +++ b/examples/server-rendering/reducer.js @@ -0,0 +1,6 @@ +import {combineReducers} from 'redux'; +import {routerStateReducer} from '../../lib'; // 'redux-router'; + +export default combineReducers({ + router: routerStateReducer +}); diff --git a/examples/server-rendering/routes.js b/examples/server-rendering/routes.js new file mode 100644 index 0000000..2187275 --- /dev/null +++ b/examples/server-rendering/routes.js @@ -0,0 +1,12 @@ +import React from 'react'; +import {Route} from 'react-router'; +import {App, Parent, Child} from './components'; + +export default ( + + + + + + +); diff --git a/examples/server-rendering/server.js b/examples/server-rendering/server.js new file mode 100644 index 0000000..74dc3a0 --- /dev/null +++ b/examples/server-rendering/server.js @@ -0,0 +1,80 @@ +import express from 'express'; +import webpack from 'webpack'; +import React from 'react'; +import {renderToString} from 'react-dom/server'; +import {Provider} from 'react-redux'; +import {createStore} from 'redux'; +import {ReduxRouter} from '../../src'; // 'redux-router' +import {reduxReactRouter, match} from '../../src/server'; // 'redux-router/server'; +import qs from 'query-string'; +import serialize from 'serialize-javascript'; + +import webpackDevMiddleware from 'webpack-dev-middleware'; +import webpackHotMiddleware from 'webpack-hot-middleware'; + +import config from './webpack.config.clientDev'; +import {MOUNT_ID} from './constants'; +import reducer from './reducer'; +import routes from './routes'; + +const app = express(); +const compiler = webpack(config); + +const getMarkup = (store) => { + const initialState = serialize(store.getState()); + + const markup = renderToString( + + + + ); + + return ` + + + Redux React Router – Server rendering Example + + +
${markup}
+ + + + + `; +}; + +app.use(webpackDevMiddleware(compiler, { + noInfo: true, + publicPath: config.output.publicPath +})); + +app.use(webpackHotMiddleware(compiler)); + +app.use((req, res) => { + const store = reduxReactRouter({ routes })(createStore)(reducer); + const query = qs.stringify(req.query); + const url = req.path + (query.length ? '?' + query : ''); + + store.dispatch(match(url, (error, redirectLocation, routerState) => { + if (redirectLocation) { + res.redirect(redirectLocation.pathname + redirectLocation.search); + } else if (error) { + console.error('Router error:', error); + res.status(500); + res.send(error); + } else if (!routerState) { + res.status(500); + } else { + res.send(getMarkup(store)); + } + })); +}); + +app.listen(3000, 'localhost', error => { + if (error) { + console.log(error); + return; + } + + console.log('Listening at http://localhost:3000'); +}); diff --git a/examples/server-rendering/webpack.config.clientDev.js b/examples/server-rendering/webpack.config.clientDev.js new file mode 100644 index 0000000..232370b --- /dev/null +++ b/examples/server-rendering/webpack.config.clientDev.js @@ -0,0 +1,27 @@ +import { + HotModuleReplacementPlugin, + NoErrorsPlugin +} from 'webpack'; + +import baseConfig from '../webpack.config.base'; +import mergeConfig from '../mergeConfig'; +import path from 'path'; + +const clientDevConfig = mergeConfig(baseConfig, { + entry: [ + 'webpack-hot-middleware/client', + './client' + ], + output: { + path: path.resolve(__dirname, 'build'), + filename: 'bundle.js', + publicPath: '/static/' + }, + plugins: [ + new HotModuleReplacementPlugin(), + new NoErrorsPlugin() + ], + devtool: 'eval' +}); + +export default clientDevConfig; diff --git a/examples/webpack.config.base.js b/examples/webpack.config.base.js index eba978d..edecc5e 100644 --- a/examples/webpack.config.base.js +++ b/examples/webpack.config.base.js @@ -1,12 +1,34 @@ +import fs from 'fs'; import path from 'path'; const PROJECT_SRC = path.resolve(__dirname, '../src'); +const babelrc = fs.readFileSync(path.join('..', '.babelrc')); +let babelLoaderQuery = {}; + +try { + babelLoaderQuery = JSON.parse(babelrc); +} catch (err) { + console.error('Error parsing .babelrc.'); + console.error(err); +} +babelLoaderQuery.plugins = babelLoaderQuery.plugins || []; +babelLoaderQuery.plugins.push('react-transform'); +babelLoaderQuery.extra = babelLoaderQuery.extra || {}; +babelLoaderQuery.extra['react-transform'] = { + transforms: [{ + transform: 'react-transform-hmr', + imports: ['react'], + locals: ['module'] + }] +}; + export default { module: { loaders: [{ test: /\.js$/, - loaders: ['babel'], + loader: 'babel', + query: babelLoaderQuery, exclude: path.resolve(__dirname, 'node_modules'), include: [ path.resolve(__dirname), @@ -16,7 +38,7 @@ export default { }, resolve: { alias: { - 'redux-react-router': PROJECT_SRC + 'redux-router': PROJECT_SRC }, extensions: ['', '.js'] } diff --git a/package.json b/package.json index 6b7c015..4a39521 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "react-dom": "^0.14.0-rc1", "react-redux": "2.x", "react-router": "1.0.0-rc1", + "react-transform-hmr": "^1.0.1", "redux": "3.x", "redux-devtools": "^2.1.0", "rimraf": "^2.4.3",