diff --git a/examples/redux/.babelrc b/examples/redux/.babelrc
new file mode 100644
index 0000000..4a129cb
--- /dev/null
+++ b/examples/redux/.babelrc
@@ -0,0 +1,10 @@
+{
+ "presets": [
+ "es2015",
+ "stage-1",
+ "react"
+ ],
+ "plugins": [
+ "transform-decorators-legacy"
+ ]
+}
diff --git a/examples/redux/client.js b/examples/redux/client.js
new file mode 100644
index 0000000..c38b5ff
--- /dev/null
+++ b/examples/redux/client.js
@@ -0,0 +1,6 @@
+import render from './render/client';
+import routes from './routes';
+import configureStore from './redux/configureStore';
+
+const store = configureStore(__FLUX_STATE__);
+render(document.getElementById('application'), routes, store);
diff --git a/examples/redux/components/App.js b/examples/redux/components/App.js
new file mode 100644
index 0000000..1dcec21
--- /dev/null
+++ b/examples/redux/components/App.js
@@ -0,0 +1,27 @@
+import React, { Component } from 'react';
+import { Link, IndexLink } from 'react-router';
+
+export default class App extends Component {
+ render() {
+ const { loading } = this.props
+ const style = {
+ opacity: loading ? 0.5 : 1,
+ transition: loading ? 'opacity 250ms ease 300ms' : 'false'
+ }
+
+ return (
+
+
React Router Redial Example
+
+ -
+ Start
+
+ -
+ Github
+
+
+ {this.props.children}
+
+ );
+ }
+}
diff --git a/examples/redux/components/Github.js b/examples/redux/components/Github.js
new file mode 100644
index 0000000..5b23d72
--- /dev/null
+++ b/examples/redux/components/Github.js
@@ -0,0 +1,32 @@
+import React, { Component } from 'react';
+import { Link } from 'react-router';
+
+export default class Github extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {value: ''};
+ }
+
+ onChange = (e) => {
+ this.setState({value: e.target.value});
+ }
+
+ render() {
+ const children = this.props.children ?
+ this.props.children :
+ (
+
+
Please select a user by typing and clicking the link
+
+
Open user!
+
+ );
+ return (
+
+
Github
+ { children }
+
+ );
+ }
+}
diff --git a/examples/redux/components/Index.js b/examples/redux/components/Index.js
new file mode 100644
index 0000000..e723bf6
--- /dev/null
+++ b/examples/redux/components/Index.js
@@ -0,0 +1,36 @@
+import React, { Component } from 'react';
+import { provideHooks } from 'redial';
+
+@provideHooks({
+ fetch: ({ setProps, getProps, force }) => new Promise((resolve) => {
+ const { color } = getProps();
+ if(!color || force) {
+ setTimeout(() => {
+ const getValue = () => Math.round(Math.random() * 255);
+ setProps({color: `rgb(${getValue()}, ${getValue()}, ${getValue()})`});
+ resolve();
+ }, 1000);
+ } else {
+ resolve();
+ }
+ }),
+ defer: ({ setProps, getProps, force }) => {
+ const { data } = getProps();
+ if(!data || force) {
+ // Will be available as this.props.data on the component
+ setProps({ data: 'Client data' })
+ }
+ }
+})
+export default class Index extends Component {
+ render() {
+ return (
+
+
React Router Redial
+
{ this.props.data }
+
+
{ JSON.stringify(this.props, null, 2) }
+
+ );
+ }
+}
diff --git a/examples/redux/components/User.js b/examples/redux/components/User.js
new file mode 100644
index 0000000..ce72560
--- /dev/null
+++ b/examples/redux/components/User.js
@@ -0,0 +1,28 @@
+import React, { Component } from 'react';
+import { provideHooks } from 'redial';
+import { connect } from 'react-redux';
+
+import fetchGithubUser from '../redux/actions/fetchGithubUser';
+
+function mapStateToProps(state, ownProps) {
+ return { user: state.githubUsers[ownProps.params.id] }
+}
+
+@provideHooks({
+ fetch: ({ params: { id }, dispatch, getState }) => {
+ if(!getState().githubUsers[id]) {
+ return dispatch(fetchGithubUser(id));
+ }
+ }
+})
+@connect(mapStateToProps)
+export default class Index extends Component {
+ render() {
+ return (
+
+
{ this.props.user.name } @{ this.props.user.login }
+
+
+ );
+ }
+}
diff --git a/examples/redux/devServer.js b/examples/redux/devServer.js
new file mode 100644
index 0000000..7e0c6ce
--- /dev/null
+++ b/examples/redux/devServer.js
@@ -0,0 +1,26 @@
+import koa from 'koa';
+import webpack from 'webpack';
+import koaWebpackDevMiddleware from 'koa-webpack-dev-middleware';
+
+import webpackConfig from './webpack.config.js';
+
+const devServer = koa();
+const compiler = webpack(webpackConfig);
+
+devServer.use(
+ koaWebpackDevMiddleware(compiler, {
+ publicPath: '/',
+ noInfo: false,
+ quiet: false
+ })
+);
+
+const hotMiddleware = require('webpack-hot-middleware')(compiler);
+devServer.use(function* (next) {
+ yield hotMiddleware.bind(null, this.req, this.res);
+ yield next;
+});
+
+devServer.listen(3001);
+
+console.log('Dev server started on http://localhost:3001');
diff --git a/examples/redux/package.json b/examples/redux/package.json
new file mode 100644
index 0000000..fc18217
--- /dev/null
+++ b/examples/redux/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "react-router-redial-redux-example",
+ "version": "0.0.0",
+ "description": "React Router Redial Redux example",
+ "scripts": {
+ "start": "babel-node server.js"
+ },
+ "license": "MIT",
+ "dependencies": {
+ "isomorphic-fetch": "2.2.1",
+ "koa": "1.2.0",
+ "nunjucks": "2.4.2",
+ "react": "15.0.2",
+ "react-dom": "15.0.2",
+ "redux-devtools": "3.2.0",
+ "redux-devtools-dock-monitor": "1.1.1",
+ "redux-devtools-log-monitor": "1.0.11",
+ "redux-logger": "2.6.1",
+ "redux-thunk": "2.1.0"
+ },
+ "devDependencies": {
+ "babel-cli": "6.8.0",
+ "babel-core": "6.8.0",
+ "babel-loader": "6.2.4",
+ "babel-plugin-transform-decorators-legacy": "1.3.4",
+ "babel-preset-es2015": "6.6.0",
+ "babel-preset-react": "6.5.0",
+ "babel-preset-stage-1": "6.5.0",
+ "koa-webpack-dev-middleware": "1.2.0",
+ "react-router": "2.4.0",
+ "react-router-redial": "0.1.3",
+ "redial": "0.4.1",
+ "webpack": "1.13.0",
+ "webpack-hot-middleware": "2.10.0"
+ }
+}
diff --git a/examples/redux/redux/actions/fetchGithubUser.js b/examples/redux/redux/actions/fetchGithubUser.js
new file mode 100644
index 0000000..af48df0
--- /dev/null
+++ b/examples/redux/redux/actions/fetchGithubUser.js
@@ -0,0 +1,12 @@
+import fetch from 'isomorphic-fetch';
+
+export default function fetchGithubUser(userId) {
+ return (dispatch) => {
+ return fetch(`https://api.github.com/users/${userId}`)
+ .then((response) => response.json())
+ .then((user) => dispatch({
+ type: 'USER_LOADED',
+ payload: user
+ }))
+ }
+}
diff --git a/examples/redux/redux/configureStore.js b/examples/redux/redux/configureStore.js
new file mode 100644
index 0000000..3107cd1
--- /dev/null
+++ b/examples/redux/redux/configureStore.js
@@ -0,0 +1,21 @@
+import { createStore, applyMiddleware, compose, combineReducers } from 'redux'
+import thunk from 'redux-thunk'
+import createLogger from 'redux-logger'
+
+export default function configureStore(initialState) {
+ const store = createStore(
+ combineReducers(require('./reducers')),
+ initialState,
+ applyMiddleware(thunk, createLogger()),
+ )
+
+ if (module.hot) {
+ // Enable Webpack hot module replacement for reducers
+ module.hot.accept('./reducers', () => {
+ const nextRootReducer = combineReducers(require('./reducers'))
+ store.replaceReducer(nextRootReducer)
+ })
+ }
+
+ return store
+}
diff --git a/examples/redux/redux/reducers/index.js b/examples/redux/redux/reducers/index.js
new file mode 100644
index 0000000..9e9829e
--- /dev/null
+++ b/examples/redux/redux/reducers/index.js
@@ -0,0 +1,11 @@
+export function githubUsers(state = {}, action) {
+ switch (action.type) {
+ case 'USER_LOADED':
+ return {
+ ...state,
+ [action.payload.login]: action.payload
+ };
+ default:
+ return state;
+ }
+}
diff --git a/examples/redux/render/client.js b/examples/redux/render/client.js
new file mode 100644
index 0000000..951eb9a
--- /dev/null
+++ b/examples/redux/render/client.js
@@ -0,0 +1,43 @@
+import { RedialContext } from 'react-router-redial';
+
+import React from 'react';
+import { render } from 'react-dom';
+import { Router, browserHistory } from 'react-router';
+import { Provider } from 'react-redux';
+
+// Render the app client-side to a given container element:
+export default (container, routes, store) => {
+ // Define extra locals to be provided to all lifecycle hooks:
+ const locals = store ? {
+ dispatch: store.dispatch,
+ getState: store.getState,
+ } : {};
+
+ let component = (
+ (
+ Loading…
}
+ />
+ )}
+ />
+ );
+
+ if (store) {
+ component = (
+
+ {component}
+
+ );
+ }
+
+ // Render app to container element:
+ render(component, container);
+};
diff --git a/examples/redux/render/server.js b/examples/redux/render/server.js
new file mode 100644
index 0000000..5151d83
--- /dev/null
+++ b/examples/redux/render/server.js
@@ -0,0 +1,42 @@
+import { triggerHooks, RedialContext } from '../../../lib/index';
+
+import React from 'react';
+import { renderToString } from 'react-dom/server';
+import { createMemoryHistory, match } from 'react-router';
+import { Provider } from 'react-redux';
+
+// Render the app server-side for a given path:
+export default (path, routes, store) => new Promise((resolve, reject) => {
+ // Set up history for router:
+ const history = createMemoryHistory(path);
+
+ // Match routes based on history object:
+ match({ routes, history }, (error, redirectLocation, renderProps) => {
+
+ // Define extra locals to be provided to all lifecycle hooks:
+ const locals = store ? {
+ dispatch: store.dispatch,
+ getState: store.getState
+ } : {};
+
+ // Wait for async data fetching to complete, then render:
+ triggerHooks({
+ renderProps,
+ locals,
+ hooks: [ 'fetch', 'done' ]
+ }).then(({ redialMap, redialProps }) => {
+ const state = store ? store.getState() : null;
+ const component = ;
+ const html = store ? renderToString(
+
+ { component }
+
+ ) : renderToString(component);
+
+ // Important that the redialProps are sent to the client
+ // by serializing it and setting it on window.__REDIAL_PROPS__
+ resolve({ html, state, redialProps });
+ })
+ .catch(reject);
+ });
+});
diff --git a/examples/redux/routes.js b/examples/redux/routes.js
new file mode 100644
index 0000000..886eab3
--- /dev/null
+++ b/examples/redux/routes.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { Router, Route, IndexRoute } from 'react-router'
+
+import App from './components/App';
+import Index from './components/Index';
+import Github from './components/Github';
+import User from './components/User';
+
+export default (
+
+
+
+
+
+
+)
diff --git a/examples/redux/server.js b/examples/redux/server.js
new file mode 100644
index 0000000..70af7fc
--- /dev/null
+++ b/examples/redux/server.js
@@ -0,0 +1,24 @@
+import koa from 'koa';
+import nunjucks from 'nunjucks';
+
+import render from './render/server';
+import routes from './routes';
+import configureStore from './redux/configureStore';
+
+require('./devServer');
+
+nunjucks.configure(__dirname);
+
+const server = koa();
+server.use(function *() {
+ const store = configureStore();
+ const result = yield render(this.url, routes, store);
+ this.body = nunjucks.render('template.html', {
+ ...result,
+ state: JSON.stringify(result.state),
+ redialProps: JSON.stringify(result.redialProps)
+ });
+});
+
+server.listen(3000);
+console.log('Server started on http://localhost:3000');
diff --git a/examples/redux/template.html b/examples/redux/template.html
new file mode 100644
index 0000000..13f1e2f
--- /dev/null
+++ b/examples/redux/template.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ {{ html | safe }}
+
+
+
+
+
+
+
diff --git a/examples/redux/webpack.config.js b/examples/redux/webpack.config.js
new file mode 100644
index 0000000..e6d138c
--- /dev/null
+++ b/examples/redux/webpack.config.js
@@ -0,0 +1,28 @@
+const path = require('path');
+const webpack = require('webpack');
+
+module.exports = {
+
+ devtool: 'source-map',
+
+ entry: path.join(__dirname, 'client.js'),
+
+ output: {
+ path: path.join(__dirname, 'build'),
+ filename: 'bundle.js',
+ publicPath: '/build/'
+ },
+
+ resolve: {
+ alias: {
+ 'react-router-redial': path.join(__dirname, '..', '..', 'src', 'index.js')
+ }
+ },
+
+ module: {
+ loaders: [
+ { test: /\.js$/, exclude: /node_modules/, loader: require.resolve('babel-loader') }
+ ]
+ },
+
+};
diff --git a/examples/simple/.babelrc b/examples/simple/.babelrc
new file mode 100644
index 0000000..4a129cb
--- /dev/null
+++ b/examples/simple/.babelrc
@@ -0,0 +1,10 @@
+{
+ "presets": [
+ "es2015",
+ "stage-1",
+ "react"
+ ],
+ "plugins": [
+ "transform-decorators-legacy"
+ ]
+}
diff --git a/examples/simple/client.js b/examples/simple/client.js
new file mode 100644
index 0000000..07e02e1
--- /dev/null
+++ b/examples/simple/client.js
@@ -0,0 +1,4 @@
+import render from './render/client';
+import routes from './routes';
+
+render(document.getElementById('application'), routes);
diff --git a/examples/simple/components/App.js b/examples/simple/components/App.js
new file mode 100644
index 0000000..1dcec21
--- /dev/null
+++ b/examples/simple/components/App.js
@@ -0,0 +1,27 @@
+import React, { Component } from 'react';
+import { Link, IndexLink } from 'react-router';
+
+export default class App extends Component {
+ render() {
+ const { loading } = this.props
+ const style = {
+ opacity: loading ? 0.5 : 1,
+ transition: loading ? 'opacity 250ms ease 300ms' : 'false'
+ }
+
+ return (
+
+
React Router Redial Example
+
+ -
+ Start
+
+ -
+ Github
+
+
+ {this.props.children}
+
+ );
+ }
+}
diff --git a/examples/simple/components/Github.js b/examples/simple/components/Github.js
new file mode 100644
index 0000000..5b23d72
--- /dev/null
+++ b/examples/simple/components/Github.js
@@ -0,0 +1,32 @@
+import React, { Component } from 'react';
+import { Link } from 'react-router';
+
+export default class Github extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {value: ''};
+ }
+
+ onChange = (e) => {
+ this.setState({value: e.target.value});
+ }
+
+ render() {
+ const children = this.props.children ?
+ this.props.children :
+ (
+
+
Please select a user by typing and clicking the link
+
+
Open user!
+
+ );
+ return (
+
+
Github
+ { children }
+
+ );
+ }
+}
diff --git a/examples/simple/components/Index.js b/examples/simple/components/Index.js
new file mode 100644
index 0000000..e723bf6
--- /dev/null
+++ b/examples/simple/components/Index.js
@@ -0,0 +1,36 @@
+import React, { Component } from 'react';
+import { provideHooks } from 'redial';
+
+@provideHooks({
+ fetch: ({ setProps, getProps, force }) => new Promise((resolve) => {
+ const { color } = getProps();
+ if(!color || force) {
+ setTimeout(() => {
+ const getValue = () => Math.round(Math.random() * 255);
+ setProps({color: `rgb(${getValue()}, ${getValue()}, ${getValue()})`});
+ resolve();
+ }, 1000);
+ } else {
+ resolve();
+ }
+ }),
+ defer: ({ setProps, getProps, force }) => {
+ const { data } = getProps();
+ if(!data || force) {
+ // Will be available as this.props.data on the component
+ setProps({ data: 'Client data' })
+ }
+ }
+})
+export default class Index extends Component {
+ render() {
+ return (
+
+
React Router Redial
+
{ this.props.data }
+
+
{ JSON.stringify(this.props, null, 2) }
+
+ );
+ }
+}
diff --git a/examples/simple/components/User.js b/examples/simple/components/User.js
new file mode 100644
index 0000000..c74f572
--- /dev/null
+++ b/examples/simple/components/User.js
@@ -0,0 +1,23 @@
+import React, { Component } from 'react';
+import { provideHooks } from 'redial';
+import fetch from 'isomorphic-fetch';
+
+@provideHooks({
+ fetch: ({ params, setProps, getProps }) => {
+ if (!getProps().user || getProps().user.login !== params.id) {
+ return fetch(`https://api.github.com/users/${params.id}`)
+ .then((response) => response.json())
+ .then((user) => setProps({ user }))
+ }
+ }
+})
+export default class Index extends Component {
+ render() {
+ return (
+
+
{ this.props.user.name } @{ this.props.user.login }
+
+
+ );
+ }
+}
diff --git a/examples/simple/devServer.js b/examples/simple/devServer.js
new file mode 100644
index 0000000..7e0c6ce
--- /dev/null
+++ b/examples/simple/devServer.js
@@ -0,0 +1,26 @@
+import koa from 'koa';
+import webpack from 'webpack';
+import koaWebpackDevMiddleware from 'koa-webpack-dev-middleware';
+
+import webpackConfig from './webpack.config.js';
+
+const devServer = koa();
+const compiler = webpack(webpackConfig);
+
+devServer.use(
+ koaWebpackDevMiddleware(compiler, {
+ publicPath: '/',
+ noInfo: false,
+ quiet: false
+ })
+);
+
+const hotMiddleware = require('webpack-hot-middleware')(compiler);
+devServer.use(function* (next) {
+ yield hotMiddleware.bind(null, this.req, this.res);
+ yield next;
+});
+
+devServer.listen(3001);
+
+console.log('Dev server started on http://localhost:3001');
diff --git a/examples/simple/package.json b/examples/simple/package.json
new file mode 100644
index 0000000..76318c1
--- /dev/null
+++ b/examples/simple/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "react-router-redial-simple-example",
+ "version": "0.0.0",
+ "description": "React Router Redial simple example",
+ "scripts": {
+ "start": "babel-node server.js"
+ },
+ "license": "MIT",
+ "dependencies": {
+ "isomorphic-fetch": "2.2.1",
+ "koa": "1.2.0",
+ "nunjucks": "2.4.2",
+ "react": "15.0.2",
+ "react-dom": "15.0.2",
+ "react-router": "2.4.0",
+ "react-router-redial": "0.1.3",
+ "redial": "0.4.1"
+ },
+ "devDependencies": {
+ "babel-cli": "6.8.0",
+ "babel-core": "6.8.0",
+ "babel-loader": "6.2.4",
+ "babel-plugin-transform-decorators-legacy": "1.3.4",
+ "babel-preset-es2015": "6.6.0",
+ "babel-preset-react": "6.5.0",
+ "babel-preset-stage-1": "6.5.0",
+ "koa-webpack-dev-middleware": "1.2.0",
+ "webpack": "1.13.0",
+ "webpack-hot-middleware": "2.10.0"
+ }
+}
diff --git a/examples/simple/render/client.js b/examples/simple/render/client.js
new file mode 100644
index 0000000..908049b
--- /dev/null
+++ b/examples/simple/render/client.js
@@ -0,0 +1,27 @@
+import { RedialContext } from 'react-router-redial';
+
+import React from 'react';
+import { render } from 'react-dom';
+import { Router, browserHistory } from 'react-router';
+
+// Render the app client-side to a given container element:
+export default (container, routes) => {
+ const component = (
+ (
+ Loading…
}
+ />
+ )}
+ />
+ );
+
+ // Render app to container element:
+ render(component, container);
+};
diff --git a/examples/simple/render/server.js b/examples/simple/render/server.js
new file mode 100644
index 0000000..f4e9906
--- /dev/null
+++ b/examples/simple/render/server.js
@@ -0,0 +1,33 @@
+import { triggerHooks, RedialContext } from '../../../lib/index';
+
+import React from 'react';
+import { renderToString } from 'react-dom/server';
+import { createMemoryHistory, match } from 'react-router';
+
+// Render the app server-side for a given path:
+export default (path, routes) => new Promise((resolve, reject) => {
+ // Set up history for router:
+ const history = createMemoryHistory(path);
+
+ // Match routes based on history object:
+ match({ routes, history }, (error, redirectLocation, renderProps) => {
+ if (error || !renderProps) {
+ return resolve({
+ html: '',
+ redialProps: null
+ });
+ }
+
+ // Wait for async data fetching to complete, then render:
+ triggerHooks({
+ renderProps,
+ hooks: [ 'fetch', 'done' ]
+ }).then(({ redialMap, redialProps }) => {
+ const html = renderToString(
+
+ );
+ resolve({ html, redialProps });
+ })
+ .catch(reject);
+ });
+});
diff --git a/examples/simple/routes.js b/examples/simple/routes.js
new file mode 100644
index 0000000..886eab3
--- /dev/null
+++ b/examples/simple/routes.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { Router, Route, IndexRoute } from 'react-router'
+
+import App from './components/App';
+import Index from './components/Index';
+import Github from './components/Github';
+import User from './components/User';
+
+export default (
+
+
+
+
+
+
+)
diff --git a/examples/simple/server.js b/examples/simple/server.js
new file mode 100644
index 0000000..b93805d
--- /dev/null
+++ b/examples/simple/server.js
@@ -0,0 +1,20 @@
+import koa from 'koa';
+import nunjucks from 'nunjucks';
+
+import render from './render/server';
+import routes from './routes';
+
+require('./devServer');
+
+nunjucks.configure(__dirname);
+
+const server = koa();
+server.use(function *() {
+ const result = yield render(this.url, routes);
+ this.body = nunjucks.render('template.html', {
+ ...result,
+ redialProps: JSON.stringify(result.redialProps)
+ });
+});
+server.listen(3000);
+console.log('Server started on http://localhost:3000');
diff --git a/examples/simple/template.html b/examples/simple/template.html
new file mode 100644
index 0000000..721fa71
--- /dev/null
+++ b/examples/simple/template.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+ {{ html | safe }}
+
+
+
+
diff --git a/examples/simple/webpack.config.js b/examples/simple/webpack.config.js
new file mode 100644
index 0000000..e6d138c
--- /dev/null
+++ b/examples/simple/webpack.config.js
@@ -0,0 +1,28 @@
+const path = require('path');
+const webpack = require('webpack');
+
+module.exports = {
+
+ devtool: 'source-map',
+
+ entry: path.join(__dirname, 'client.js'),
+
+ output: {
+ path: path.join(__dirname, 'build'),
+ filename: 'bundle.js',
+ publicPath: '/build/'
+ },
+
+ resolve: {
+ alias: {
+ 'react-router-redial': path.join(__dirname, '..', '..', 'src', 'index.js')
+ }
+ },
+
+ module: {
+ loaders: [
+ { test: /\.js$/, exclude: /node_modules/, loader: require.resolve('babel-loader') }
+ ]
+ },
+
+};