diff --git a/package.json b/package.json
index f2e49b008..ce8f29661 100644
--- a/package.json
+++ b/package.json
@@ -88,7 +88,6 @@
"compression": "^1.6.0",
"express": "^4.13.3",
"express-session": "^1.12.1",
- "history": "1.17.0",
"file-loader": "^0.8.5",
"http-proxy": "^1.12.0",
"invariant": "^2.2.0",
@@ -99,19 +98,19 @@
"multireducer": "^2.0.0",
"piping": "^0.3.0",
"pretty-error": "^1.2.0",
- "query-string": "^3.0.0",
"react": "^0.14.2",
"react-bootstrap": "^0.28.1",
"react-dom": "^0.14.1",
"react-helmet": "^2.2.0",
"react-inline-css": "^2.0.0",
"react-redux": "^4.0.0",
- "react-router": "1.0.3",
- "react-router-bootstrap": "^0.19.3",
+ "react-router": "2.0.0",
+ "react-router-bootstrap": "^0.20.1",
+ "react-router-redux": "^3.0.0",
"redux": "^3.0.4",
+ "redux-async-connect": "^0.1.13",
"redux-form": "^3.0.12",
- "redux-router": "1.0.0-beta5",
- "scroll-behavior": "^0.3.0",
+ "scroll-behavior": "^0.3.2",
"serialize-javascript": "^1.1.2",
"serve-favicon": "^2.3.0",
"socket.io": "^1.3.7",
diff --git a/src/client.js b/src/client.js
index 948d9fc6f..73f621426 100644
--- a/src/client.js
+++ b/src/client.js
@@ -4,25 +4,20 @@
import 'babel/polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
-import createHistory from 'history/lib/createBrowserHistory';
-import useScroll from 'scroll-behavior/lib/useStandardScroll';
import createStore from './redux/create';
import ApiClient from './helpers/ApiClient';
import io from 'socket.io-client';
import {Provider} from 'react-redux';
-import {reduxReactRouter, ReduxRouter} from 'redux-router';
+import { Router, browserHistory } from 'react-router';
+import { ReduxAsyncConnect } from 'redux-async-connect';
+import useScroll from 'scroll-behavior/lib/useStandardScroll';
import getRoutes from './routes';
-import makeRouteHooksSafe from './helpers/makeRouteHooksSafe';
const client = new ApiClient();
-
-// Three different types of scroll behavior available.
-// Documented here: https://github.com/rackt/scroll-behavior
-const scrollableHistory = useScroll(createHistory);
-
+const history = useScroll(() => browserHistory)();
const dest = document.getElementById('content');
-const store = createStore(reduxReactRouter, makeRouteHooksSafe(getRoutes), scrollableHistory, client, window.__data);
+const store = createStore(history, client, window.__data);
function initSocket() {
const socket = io('', {path: '/ws'});
@@ -40,7 +35,11 @@ function initSocket() {
global.socket = initSocket();
const component = (
-
+
+
+ } history={history}>
+ {getRoutes(store)}
+
);
ReactDOM.render(
diff --git a/src/components/__tests__/InfoBar-test.js b/src/components/__tests__/InfoBar-test.js
index 6652d6f01..22d458046 100644
--- a/src/components/__tests__/InfoBar-test.js
+++ b/src/components/__tests__/InfoBar-test.js
@@ -4,8 +4,7 @@ import {renderIntoDocument} from 'react-addons-test-utils';
import { expect} from 'chai';
import { InfoBar } from 'components';
import { Provider } from 'react-redux';
-import {reduxReactRouter} from 'redux-router';
-import createHistory from 'history/lib/createMemoryHistory';
+import { browserHistory } from 'react-router';
import createStore from 'redux/create';
import ApiClient from 'helpers/ApiClient';
const client = new ApiClient();
@@ -22,8 +21,7 @@ describe('InfoBar', () => {
}
}
};
-
- const store = createStore(reduxReactRouter, null, createHistory, client, mockStore);
+ const store = createStore(browserHistory, client, mockStore);
const renderer = renderIntoDocument(
diff --git a/src/containers/App/App.js b/src/containers/App/App.js
index 5222ef2f7..ef117f62c 100644
--- a/src/containers/App/App.js
+++ b/src/containers/App/App.js
@@ -9,25 +9,12 @@ import Helmet from 'react-helmet';
import { isLoaded as isInfoLoaded, load as loadInfo } from 'redux/modules/info';
import { isLoaded as isAuthLoaded, load as loadAuth, logout } from 'redux/modules/auth';
import { InfoBar } from 'components';
-import { pushState } from 'redux-router';
-import connectData from 'helpers/connectData';
+import { routeActions } from 'react-router-redux';
import config from '../../config';
-function fetchData(getState, dispatch) {
- const promises = [];
- if (!isInfoLoaded(getState())) {
- promises.push(dispatch(loadInfo()));
- }
- if (!isAuthLoaded(getState())) {
- promises.push(dispatch(loadAuth()));
- }
- return Promise.all(promises);
-}
-
-@connectData(fetchData)
@connect(
state => ({user: state.auth.user}),
- {logout, pushState})
+ {logout, pushState: routeActions.push})
export default class App extends Component {
static propTypes = {
children: PropTypes.object.isRequired,
@@ -43,17 +30,31 @@ export default class App extends Component {
componentWillReceiveProps(nextProps) {
if (!this.props.user && nextProps.user) {
// login
- this.props.pushState(null, '/loginSuccess');
+ this.props.pushState('/loginSuccess');
} else if (this.props.user && !nextProps.user) {
// logout
- this.props.pushState(null, '/');
+ this.props.pushState('/');
+ }
+ }
+
+ static reduxAsyncConnect(params, store) {
+ const {dispatch, getState} = store;
+ const promises = [];
+
+ if (!isInfoLoaded(getState())) {
+ promises.push(dispatch(loadInfo()));
+ }
+ if (!isAuthLoaded(getState())) {
+ promises.push(dispatch(loadAuth()));
}
+
+ return Promise.all(promises);
}
handleLogout = (event) => {
event.preventDefault();
this.props.logout();
- }
+ };
render() {
const {user} = this.props;
diff --git a/src/containers/Widgets/Widgets.js b/src/containers/Widgets/Widgets.js
index 001e3a758..e02c0e84c 100644
--- a/src/containers/Widgets/Widgets.js
+++ b/src/containers/Widgets/Widgets.js
@@ -4,16 +4,8 @@ import {connect} from 'react-redux';
import * as widgetActions from 'redux/modules/widgets';
import {isLoaded, load as loadWidgets} from 'redux/modules/widgets';
import {initializeWithKey} from 'redux-form';
-import connectData from 'helpers/connectData';
import { WidgetForm } from 'components';
-function fetchDataDeferred(getState, dispatch) {
- if (!isLoaded(getState())) {
- return dispatch(loadWidgets());
- }
-}
-
-@connectData(null, fetchDataDeferred)
@connect(
state => ({
widgets: state.widgets.data,
@@ -31,6 +23,13 @@ export default class Widgets extends Component {
editing: PropTypes.object.isRequired,
load: PropTypes.func.isRequired,
editStart: PropTypes.func.isRequired
+ };
+
+ static reduxAsyncConnect(params, store) {
+ const {dispatch, getState} = store;
+ if (!isLoaded(getState())) {
+ return dispatch(loadWidgets());
+ }
}
render() {
diff --git a/src/helpers/Html.js b/src/helpers/Html.js
index 9e415a994..ebf5ba436 100644
--- a/src/helpers/Html.js
+++ b/src/helpers/Html.js
@@ -17,7 +17,7 @@ export default class Html extends Component {
assets: PropTypes.object,
component: PropTypes.node,
store: PropTypes.object
- }
+ };
render() {
const {assets, component, store} = this.props;
diff --git a/src/helpers/__tests__/connectData-test.js b/src/helpers/__tests__/connectData-test.js
deleted file mode 100644
index d61d73bf4..000000000
--- a/src/helpers/__tests__/connectData-test.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { expect } from 'chai';
-import React from 'react';
-import { div } from 'react-dom';
-import connectData from '../connectData';
-
-describe('connectData', () => {
- let fetchData;
- let fetchDataDeferred;
- let WrappedComponent;
- let DataComponent;
-
- beforeEach(() => {
- fetchData = 'fetchDataFunction';
- fetchDataDeferred = 'fetchDataDeferredFunction';
-
- WrappedComponent = () =>
- ;
-
- DataComponent = connectData(fetchData, fetchDataDeferred)(WrappedComponent);
- });
-
- it('should set fetchData as a static property of the final component', () => {
- expect(DataComponent.fetchData).to.equal(fetchData);
- });
-
- it('should set fetchDataDeferred as a static property of the final component', () => {
- expect(DataComponent.fetchDataDeferred).to.equal(fetchDataDeferred);
- });
-});
diff --git a/src/helpers/__tests__/getDataDependencies-test.js b/src/helpers/__tests__/getDataDependencies-test.js
deleted file mode 100644
index fb2a066f1..000000000
--- a/src/helpers/__tests__/getDataDependencies-test.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import { expect } from 'chai';
-import React from 'react';
-import { div } from 'react-dom';
-import getDataDependencies from '../getDataDependencies';
-
-describe('getDataDependencies', () => {
- let getState;
- let dispatch;
- let location;
- let params;
- let CompWithFetchData;
- let CompWithNoData;
- let CompWithFetchDataDeferred;
- const NullComponent = null;
-
- beforeEach(() => {
- getState = 'getState';
- dispatch = 'dispatch';
- location = 'location';
- params = 'params';
-
- CompWithNoData = () =>
- ;
-
- CompWithFetchData = () =>
- ;
-
- CompWithFetchData.fetchData = (_getState, _dispatch, _location, _params) => {
- return `fetchData ${_getState} ${_dispatch} ${_location} ${_params}`;
- };
- CompWithFetchDataDeferred = () =>
- ;
-
- CompWithFetchDataDeferred.fetchDataDeferred = (_getState, _dispatch, _location, _params) => {
- return `fetchDataDeferred ${_getState} ${_dispatch} ${_location} ${_params}`;
- };
- });
-
- it('should get fetchDatas', () => {
- const deps = getDataDependencies([
- NullComponent,
- CompWithFetchData,
- CompWithNoData,
- CompWithFetchDataDeferred
- ], getState, dispatch, location, params);
-
- expect(deps).to.deep.equal([
- 'fetchData getState dispatch location params'
- ]);
- });
-
- it('should get fetchDataDeferreds', () => {
- const deps = getDataDependencies([
- NullComponent,
- CompWithFetchData,
- CompWithNoData,
- CompWithFetchDataDeferred
- ], getState, dispatch, location, params, true);
-
- expect(deps).to.deep.equal([
- 'fetchDataDeferred getState dispatch location params'
- ]);
- });
-});
diff --git a/src/helpers/__tests__/getStatusFromRoutes-test.js b/src/helpers/__tests__/getStatusFromRoutes-test.js
deleted file mode 100644
index db41083e6..000000000
--- a/src/helpers/__tests__/getStatusFromRoutes-test.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import { expect } from 'chai';
-import getStatusFromRoutes from '../getStatusFromRoutes';
-
-describe('getStatusFromRoutes', () => {
- it('should return null when no routes have status code', () => {
- const status = getStatusFromRoutes([
- {}, {}
- ]);
-
- expect(status).to.equal(null);
- });
-
- it('should return the only status code', () => {
- const status = getStatusFromRoutes([
- {status: 404}
- ]);
-
- expect(status).to.equal(404);
- });
-
- it('should return the only status code when other routes have none', () => {
- const status = getStatusFromRoutes([
- {status: 404}, {}, {}
- ]);
-
- expect(status).to.equal(404);
- });
-
- it('should return the last status code when later routes have none', () => {
- const status = getStatusFromRoutes([
- {status: 200}, {status: 404}, {}
- ]);
-
- expect(status).to.equal(404);
- });
-
- it('should return the last status code when previous routes have one', () => {
- const status = getStatusFromRoutes([
- {status: 200}, {}, {status: 404}
- ]);
-
- expect(status).to.equal(404);
- });
-
- it('should return the last status code', () => {
- const status = getStatusFromRoutes([
- {}, {}, {status: 404}
- ]);
-
- expect(status).to.equal(404);
- });
-});
diff --git a/src/helpers/__tests__/makeRouteHooksSafe-test.js b/src/helpers/__tests__/makeRouteHooksSafe-test.js
deleted file mode 100644
index 9c49dd7e9..000000000
--- a/src/helpers/__tests__/makeRouteHooksSafe-test.js
+++ /dev/null
@@ -1,110 +0,0 @@
-import { expect } from 'chai';
-import React from 'react';
-import { IndexRoute, Route } from 'react-router';
-import makeRouteHooksSafe from '../makeRouteHooksSafe';
-
-
-describe('makeRouteHooksSafe', () => {
- it('should work with JSX routes', () => {
- const onEnter = () => {
- throw new Error('Shouldn\'t call onEnter');
- };
-
- const getRoutes = makeRouteHooksSafe(() => {
- return (
-
-
-
-
-
-
-
-
- );
- });
-
- const routes = getRoutes(null);
-
- expect(routes[0].indexRoute.onEnter).to.not.throw(Error);
- expect(routes[0].childRoutes[1].onEnter).to.not.throw(Error);
- expect(routes[0].childRoutes[1].childRoutes[1].onEnter).to.not.throw(Error);
- });
-
- it('should work with JS routes', () => {
- const onEnter = () => {
- throw new Error('Shouldn\'t call onEnter');
- };
-
- const getRoutes = makeRouteHooksSafe(() => {
- return {
- path: '/',
- indexRoute: {
- onEnter: onEnter
- },
- onEnter: onEnter,
- childRoutes: [
- {path: '1'},
- {
- onEnter: onEnter,
- childRoutes: [
- {path: '2'},
- {path: '3'}
- ],
- }
- ]
- };
- });
-
- const routes = getRoutes(null);
-
- expect(routes[0].indexRoute.onEnter).to.not.throw(Error);
- expect(routes[0].onEnter).to.not.throw(Error);
- expect(routes[0].childRoutes[1].onEnter).to.not.throw(Error);
- });
-
- it('should call onEnter if store is initialised', (done) => {
- const store = {
- getState: () => {}
- };
-
- const getRoutes = makeRouteHooksSafe(() => {
- return {
- onEnter: () => {
- done();
- }
- };
- });
-
- const routes = getRoutes(store);
-
- routes[0].onEnter();
- });
-
- it('should call callback', (done) => {
- const getRoutes = makeRouteHooksSafe(() => {
- return {
- onEnter: (nextState, replaceState, cb) => {} // eslint-disable-line no-unused-vars
- };
- });
-
- const routes = getRoutes(null);
-
- routes[0].onEnter(null, null, done);
- });
-
- it('should not call callback', () => {
- const callback = () => {
- throw new Error('Should not be called');
- };
-
- const getRoutes = makeRouteHooksSafe(() => {
- return {
- onEnter: (nextState, replaceState) => {} // eslint-disable-line no-unused-vars
- };
- });
-
- const routes = getRoutes(null);
-
- routes[0].onEnter(null, null, callback);
- });
-});
diff --git a/src/helpers/connectData.js b/src/helpers/connectData.js
deleted file mode 100644
index a67510292..000000000
--- a/src/helpers/connectData.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React, { Component } from 'react';
-
-/*
- Note:
- When this decorator is used, it MUST be the first (outermost) decorator.
- Otherwise, we cannot find and call the fetchData and fetchDataDeffered methods.
-*/
-
-export default function connectData(fetchData, fetchDataDeferred) {
- return function wrapWithFetchData(WrappedComponent) {
- class ConnectData extends Component {
-
- static fetchData = fetchData;
- static fetchDataDeferred = fetchDataDeferred;
-
- render() {
- return ;
- }
- }
-
- return ConnectData;
- };
-}
diff --git a/src/helpers/getDataDependencies.js b/src/helpers/getDataDependencies.js
deleted file mode 100644
index 9559c51f3..000000000
--- a/src/helpers/getDataDependencies.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * 1. Skip holes in route component chain and
- * only consider components that implement
- * fetchData or fetchDataDeferred
- *
- * 2. Pull out fetch data methods
- *
- * 3. Call fetch data methods and gather promises
- */
-export default (components, getState, dispatch, location, params, deferred) => {
- const methodName = deferred ? 'fetchDataDeferred' : 'fetchData';
-
- return components
- .filter((component) => component && component[methodName]) // 1
- .map((component) => component[methodName]) // 2
- .map(fetchData =>
- fetchData(getState, dispatch, location, params)); // 3
-};
diff --git a/src/helpers/getStatusFromRoutes.js b/src/helpers/getStatusFromRoutes.js
deleted file mode 100644
index af952a2a8..000000000
--- a/src/helpers/getStatusFromRoutes.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * Return the status code from the last matched route with a status property.
- *
- * @param matchedRoutes
- * @returns {Number|null}
- */
-export default (matchedRoutes) => {
- return matchedRoutes.reduce((prev, cur) => cur.status || prev, null);
-};
diff --git a/src/helpers/makeRouteHooksSafe.js b/src/helpers/makeRouteHooksSafe.js
deleted file mode 100644
index 7c9f8c539..000000000
--- a/src/helpers/makeRouteHooksSafe.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import { createRoutes } from 'react-router/lib/RouteUtils';
-
-// Wrap the hooks so they don't fire if they're called before
-// the store is initialised. This only happens when doing the first
-// client render of a route that has an onEnter hook
-function makeHooksSafe(routes, store) {
- if (Array.isArray(routes)) {
- return routes.map((route) => makeHooksSafe(route, store));
- }
-
- const onEnter = routes.onEnter;
-
- if (onEnter) {
- routes.onEnter = function safeOnEnter(...args) {
- try {
- store.getState();
- } catch (err) {
- if (onEnter.length === 3) {
- args[2]();
- }
-
- // There's no store yet so ignore the hook
- return;
- }
-
- onEnter.apply(null, args);
- };
- }
-
- if (routes.childRoutes) {
- makeHooksSafe(routes.childRoutes, store);
- }
-
- if (routes.indexRoute) {
- makeHooksSafe(routes.indexRoute, store);
- }
-
- return routes;
-}
-
-export default function makeRouteHooksSafe(_getRoutes) {
- return (store) => makeHooksSafe(createRoutes(_getRoutes(store)), store);
-}
diff --git a/src/redux/create.js b/src/redux/create.js
index 6a08c9dbb..0f52727c7 100644
--- a/src/redux/create.js
+++ b/src/redux/create.js
@@ -1,9 +1,12 @@
import { createStore as _createStore, applyMiddleware, compose } from 'redux';
import createMiddleware from './middleware/clientMiddleware';
-import transitionMiddleware from './middleware/transitionMiddleware';
+import { syncHistory } from 'react-router-redux';
-export default function createStore(reduxReactRouter, getRoutes, createHistory, client, data) {
- const middleware = [createMiddleware(client), transitionMiddleware];
+export default function createStore(history, client, data) {
+ // Sync dispatched route actions to the history
+ const reduxRouterMiddleware = syncHistory(history);
+
+ const middleware = [createMiddleware(client), reduxRouterMiddleware];
let finalCreateStore;
if (__DEVELOPMENT__ && __CLIENT__ && __DEVTOOLS__) {
@@ -18,11 +21,11 @@ export default function createStore(reduxReactRouter, getRoutes, createHistory,
finalCreateStore = applyMiddleware(...middleware)(_createStore);
}
- finalCreateStore = reduxReactRouter({ getRoutes, createHistory })(finalCreateStore);
-
const reducer = require('./modules/reducer');
const store = finalCreateStore(reducer, data);
+ reduxRouterMiddleware.listenForReplays(store);
+
if (__DEVELOPMENT__ && module.hot) {
module.hot.accept('./modules/reducer', () => {
store.replaceReducer(require('./modules/reducer'));
diff --git a/src/redux/middleware/clientMiddleware.js b/src/redux/middleware/clientMiddleware.js
index 7ca18f95e..c3403c166 100644
--- a/src/redux/middleware/clientMiddleware.js
+++ b/src/redux/middleware/clientMiddleware.js
@@ -12,13 +12,17 @@ export default function clientMiddleware(client) {
const [REQUEST, SUCCESS, FAILURE] = types;
next({...rest, type: REQUEST});
- return promise(client).then(
+
+ const actionPromise = promise(client);
+ actionPromise.then(
(result) => next({...rest, result, type: SUCCESS}),
(error) => next({...rest, error, type: FAILURE})
).catch((error)=> {
console.error('MIDDLEWARE ERROR:', error);
next({...rest, error, type: FAILURE});
});
+
+ return actionPromise;
};
};
}
diff --git a/src/redux/middleware/transitionMiddleware.js b/src/redux/middleware/transitionMiddleware.js
deleted file mode 100644
index 5532f89e2..000000000
--- a/src/redux/middleware/transitionMiddleware.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import {ROUTER_DID_CHANGE} from 'redux-router/lib/constants';
-import getDataDependencies from '../../helpers/getDataDependencies';
-
-const locationsAreEqual = (locA, locB) => (locA.pathname === locB.pathname) && (locA.search === locB.search);
-
-export default ({getState, dispatch}) => next => action => {
- if (action.type === ROUTER_DID_CHANGE) {
- if (getState().router && locationsAreEqual(action.payload.location, getState().router.location)) {
- return next(action);
- }
-
- const {components, location, params} = action.payload;
- const promise = new Promise((resolve) => {
- const doTransition = () => {
- next(action);
- Promise.all(getDataDependencies(components, getState, dispatch, location, params, true))
- .then(resolve)
- .catch(error => {
- // TODO: You may want to handle errors for fetchDataDeferred here
- console.warn('Warning: Error in fetchDataDeferred', error);
- return resolve();
- });
- };
-
- Promise.all(getDataDependencies(components, getState, dispatch, location, params))
- .then(doTransition)
- .catch(error => {
- // TODO: You may want to handle errors for fetchData here
- console.warn('Warning: Error in fetchData', error);
- return doTransition();
- });
- });
-
- if (__SERVER__) {
- // router state is null until ReduxRouter is created so we can use this to store
- // our promise to let the server know when it can render
- getState().router = promise;
- }
-
- return promise;
- }
-
- return next(action);
-};
diff --git a/src/redux/modules/reducer.js b/src/redux/modules/reducer.js
index 6efb9e7cb..d260a1632 100644
--- a/src/redux/modules/reducer.js
+++ b/src/redux/modules/reducer.js
@@ -1,6 +1,7 @@
import { combineReducers } from 'redux';
import multireducer from 'multireducer';
-import { routerStateReducer } from 'redux-router';
+import { routeReducer } from 'react-router-redux';
+import {reducer as reduxAsyncConnect} from 'redux-async-connect';
import auth from './auth';
import counter from './counter';
@@ -9,7 +10,8 @@ import info from './info';
import widgets from './widgets';
export default combineReducers({
- router: routerStateReducer,
+ routing: routeReducer,
+ reduxAsyncConnect,
auth,
form,
multireducer: multireducer({
diff --git a/src/server.js b/src/server.js
index c95a092ec..a806783f7 100644
--- a/src/server.js
+++ b/src/server.js
@@ -12,13 +12,11 @@ import Html from './helpers/Html';
import PrettyError from 'pretty-error';
import http from 'http';
-import {ReduxRouter} from 'redux-router';
-import createHistory from 'history/lib/createMemoryHistory';
-import {reduxReactRouter, match} from 'redux-router/server';
+import { match } from 'react-router';
+import { ReduxAsyncConnect, loadOnServer } from 'redux-async-connect';
+import createHistory from 'react-router/lib/createMemoryHistory';
import {Provider} from 'react-redux';
-import qs from 'query-string';
import getRoutes from './routes';
-import getStatusFromRoutes from './helpers/getStatusFromRoutes';
const targetUrl = 'http://' + config.apiHost + ':' + config.apiPort;
const pretty = new PrettyError();
@@ -68,8 +66,9 @@ app.use((req, res) => {
webpackIsomorphicTools.refresh();
}
const client = new ApiClient(req);
+ const history = createHistory(req.originalUrl);
- const store = createStore(reduxReactRouter, getRoutes, createHistory, client);
+ const store = createStore(history, client);
function hydrateOnClient() {
res.send('\n' +
@@ -81,43 +80,32 @@ app.use((req, res) => {
return;
}
- store.dispatch(match(req.originalUrl, (error, redirectLocation, routerState) => {
+ match({ history, routes: getRoutes(store), location: req.originalUrl }, (error, redirectLocation, renderProps) => {
if (redirectLocation) {
res.redirect(redirectLocation.pathname + redirectLocation.search);
} else if (error) {
console.error('ROUTER ERROR:', pretty.render(error));
res.status(500);
hydrateOnClient();
- } else if (!routerState) {
- res.status(500);
- hydrateOnClient();
- } else {
- // Workaround redux-router query string issue:
- // https://github.com/rackt/redux-router/issues/106
- if (routerState.location.search && !routerState.location.query) {
- routerState.location.query = qs.parse(routerState.location.search);
- }
-
- store.getState().router.then(() => {
+ } else if (renderProps) {
+ loadOnServer(renderProps, store, {client}).then(() => {
const component = (
-
+
);
- const status = getStatusFromRoutes(routerState.routes);
- if (status) {
- res.status(status);
- }
+ res.status(200);
+
+ global.navigator = {userAgent: req.headers['user-agent']};
+
res.send('\n' +
ReactDOM.renderToString());
- }).catch((err) => {
- console.error('DATA FETCHING ERROR:', pretty.render(err));
- res.status(500);
- hydrateOnClient();
});
+ } else {
+ res.status(404).send('Not found');
}
- }));
+ });
});
if (config.port) {