Skip to content

Commit 1e63349

Browse files
committed
react-router 4 & react-loadable & redial
1 parent c67852c commit 1e63349

35 files changed

+530
-519
lines changed

.eslintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"prefer-promise-reject-errors": "warn",
2222
"prefer-template": "warn",
2323
"react/forbid-prop-types": "warn",
24-
"react/jsx-closing-tag-location": ["warn", "line-aligned"],
24+
"react/jsx-closing-tag-location": "off",
2525
"react/jsx-filename-extension": "off",
2626
"react/jsx-no-target-blank": "warn",
2727
"react/no-multi-comp": ["error", {"ignoreStateless": true}],

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ We also spit out the `redux` state into a global `window.__data` variable in the
134134

135135
#### Server-side Data Fetching
136136

137-
The [redux-connect](https://www.npmjs.com/package/redux-connect) package exposes an API to return promises that need to be fulfilled before a route is rendered. It exposes a `<ReduxAsyncConnect />` container, which wraps our render tree on both [server](https://github.com/bertho-zero/react-redux-universal-hot-example/blob/master/src/server.js) and [client](https://github.com/bertho-zero/react-redux-universal-hot-example/blob/master/src/client.js). More documentation is available on the [redux-connect](https://www.npmjs.com/package/redux-connect) page.
137+
The [redial](https://www.npmjs.com/package/redial) package exposes an API to return promises that need to be fulfilled before a route is rendered. It exposes a `<ReduxAsyncConnect />` container, which wraps our render tree on both [server](https://github.com/bertho-zero/react-redux-universal-hot-example/blob/master/src/server.js) and [client](https://github.com/bertho-zero/react-redux-universal-hot-example/blob/master/src/client.js). More documentation is available on the [redial](https://www.npmjs.com/package/redial) page.
138138

139139
#### Client Side
140140

api/api.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ app
2727
}))
2828
.use(bodyParser.urlencoded({ extended: true }))
2929
.use(bodyParser.json())
30+
// Core
3031
.configure(hooks())
3132
.configure(rest())
3233
.configure(socketio({ path: '/ws' }))
3334
.configure(auth)
3435
.use(actionHandler(app))
3536
.configure(services)
37+
// Final handlers
3638
.use(notFound())
3739
.use(logger(app))
3840
.use(errorHandler({

api/middleware/actionHandler.js

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,20 @@ export default function actionHandler(app) {
1414

1515
req.app = app;
1616

17-
// TODO use next(error) instead of catchError ?
18-
const catchError = async error => {
19-
console.error('API ERROR:', pretty.render(error));
20-
res.status(error.status || 500).json(error);
21-
};
22-
2317
if (action) {
2418
try {
25-
try {
26-
const result = await action(req, params);
27-
if (result instanceof Function) {
28-
result(res);
29-
} else {
30-
res.json(result);
31-
}
32-
} catch (reason) {
33-
if (reason && reason.redirect) {
34-
return res.redirect(reason.redirect);
35-
}
36-
return catchError(reason);
19+
const result = await action(req, params);
20+
if (result instanceof Function) {
21+
result(res);
22+
} else {
23+
res.json(result);
3724
}
3825
} catch (error) {
39-
return catchError(error);
26+
if (error && error.redirect) {
27+
return res.redirect(error.redirect);
28+
}
29+
console.error('API ERROR:', pretty.render(error));
30+
res.status(error.status || 500).json(error);
4031
}
4132
} else {
4233
next();

package.json

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
{
22
"name": "react-redux-universal-hot-example",
33
"description": "Example of an isomorphic (universal) webapp using react redux and hot reloading",
4-
"author": "Erik Rasmussen <[email protected]> (http://github.com/erikras)",
4+
"author": "Kévin Berthommier <[email protected]> (http://github.com/bertho-zero)",
5+
"contributors": [
6+
"Erik Rasmussen <[email protected]> (http://github.com/erikras)"
7+
],
58
"license": "MIT",
69
"version": "0.9.0",
710
"repository": {
@@ -119,7 +122,7 @@
119122
},
120123
"dependencies": {
121124
"async": "^2.1.4",
122-
"axios": "^0.16.2",
125+
"axios": "^0.17.0",
123126
"babel-core": "^6.26.0",
124127
"babel-plugin-add-module-exports": "^0.2.1",
125128
"babel-plugin-dynamic-import-node": "^1.1.0",
@@ -166,17 +169,19 @@
166169
"react-bootstrap": "^0.31.2",
167170
"react-dom": "^16.0.0",
168171
"react-helmet": "^5.0.3",
169-
"react-loadable": "^5.2.2",
172+
"react-loadable": "^5.3.0",
170173
"react-redux": "^5.0.6",
171174
"react-router": "^4.2.0",
172175
"react-router-bootstrap": "^0.24.4",
176+
"react-router-config": "^1.0.0-beta.4",
173177
"react-router-dom": "^4.2.2",
174178
"react-router-redux": "^5.0.0-alpha.6",
175-
"react-router-scroll": "^0.4.1",
179+
"recompose": "^0.26.0",
180+
"redial": "^0.5.0",
176181
"redux": "^3.6.0",
177-
"redux-auth-wrapper": "^1.0.0",
178-
"redux-connect": "^6.0.0",
182+
"redux-auth-wrapper": "^2.0.2",
179183
"redux-form": "^7.1.1",
184+
"redux-logger": "^3.0.6",
180185
"redux-persist": "^4.8.3",
181186
"serialize-javascript": "^1.3.0",
182187
"serve-favicon": "^2.3.2",
@@ -215,7 +220,7 @@
215220
"less": "^3.0.0-alpha.3",
216221
"less-loader": "^4.0.3",
217222
"lighthouse": "^2.4.0",
218-
"lint-staged": "^4.0.3",
223+
"lint-staged": "^5.0.0",
219224
"mocha": "^4.0.1",
220225
"node-sass": "^4.5.0",
221226
"piping": "^1.0.0-rc.4",

src/app.js

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import React, { Component } from 'react';
2-
import PropTypes from 'prop-types';
31
import feathers from 'feathers/client';
42
import hooks from 'feathers-hooks';
53
import rest from 'feathers-rest/client';
@@ -42,20 +40,3 @@ export function createApp(req) {
4240

4341
return configureApp(socketio(socket));
4442
}
45-
46-
export function withApp(WrappedComponent) {
47-
// eslint-disable-next-line react/prefer-stateless-function
48-
class WithAppComponent extends Component {
49-
static contextTypes = {
50-
app: PropTypes.object.isRequired,
51-
restApp: PropTypes.object.isRequired
52-
};
53-
54-
render() {
55-
const { app, restApp } = this.context;
56-
return <WrappedComponent {...this.props} app={app} restApp={restApp} />;
57-
}
58-
}
59-
60-
return WithAppComponent;
61-
}

src/client.js

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,33 @@
44
import 'babel-polyfill';
55
import React from 'react';
66
import ReactDOM from 'react-dom';
7-
import { Provider } from 'react-redux';
87
import { ConnectedRouter } from 'react-router-redux';
8+
import { renderRoutes } from 'react-router-config';
9+
import { trigger } from 'redial';
910
import createBrowserHistory from 'history/createBrowserHistory';
1011
import Loadable from 'react-loadable';
11-
// import { bindActionCreators } from 'redux';
12-
// import { ReduxAsyncConnect } from 'redux-connect';
1312
import { AppContainer as HotEnabler } from 'react-hot-loader';
14-
// import { useScroll } from 'react-router-scroll';
1513
import { getStoredState } from 'redux-persist';
1614
import localForage from 'localforage';
1715
import { socket, createApp } from 'app';
18-
// import { Provider } from 'components';
19-
import createStore from './redux/create';
20-
import apiClient from './helpers/apiClient';
21-
import routes from './routes';
22-
import isOnline from './utils/isOnline';
23-
24-
const offlinePersistConfig = {
16+
import createStore from 'redux/create';
17+
import apiClient from 'helpers/apiClient';
18+
import routes from 'routes';
19+
import isOnline from 'utils/isOnline';
20+
import asyncMatchRoutes from 'utils/asyncMatchRoutes';
21+
import { ReduxAsyncConnect, Provider } from 'components';
22+
23+
const persistConfig = {
2524
storage: localForage,
26-
whitelist: ['auth', 'info', 'chat'],
25+
whitelist: ['auth', 'info', 'chat']
2726
};
2827

29-
const client = apiClient();
28+
const dest = document.getElementById('content');
29+
3030
const app = createApp();
3131
const restApp = createApp('rest');
32-
const dest = document.getElementById('content');
32+
const client = apiClient();
33+
const providers = { app, restApp, client };
3334

3435
function initSocket() {
3536
socket.on('news', data => {
@@ -46,7 +47,7 @@ function initSocket() {
4647
global.socket = initSocket();
4748

4849
(async () => {
49-
const storedData = await getStoredState(offlinePersistConfig);
50+
const storedData = await getStoredState(persistConfig);
5051
const online = await (window.__data ? true : isOnline());
5152

5253
if (online) {
@@ -56,44 +57,41 @@ global.socket = initSocket();
5657

5758
const history = createBrowserHistory();
5859
const data = !online ? { ...storedData, ...window.__data, online } : { ...window.__data, online };
59-
const store = createStore(history, { client, app, restApp }, data, offlinePersistConfig);
60-
// const history = syncHistoryWithStore(browserHistory, store);
61-
62-
// const redirect = bindActionCreators(replace, store.dispatch);
63-
//
64-
// const renderRouter = props => (
65-
// <ReduxAsyncConnect
66-
// {...props}
67-
// helpers={{
68-
// client,
69-
// app,
70-
// restApp,
71-
// redirect
72-
// }}
73-
// filter={item => !item.deferred}
74-
// render={applyRouterMiddleware(useScroll())}
75-
// />
76-
// );
77-
78-
const hydrate = _routes => {
60+
const store = createStore({
61+
history,
62+
data,
63+
helpers: providers,
64+
persistConfig
65+
});
66+
67+
const hydrate = async _routes => {
68+
const components = await asyncMatchRoutes(_routes, history.location.pathname);
69+
await trigger('fetch', components, { store, ...providers });
70+
await trigger('defer', components, { store, ...providers });
7971
ReactDOM.hydrate(
8072
<HotEnabler>
81-
<Provider store={store}>
82-
<ConnectedRouter history={history}>{_routes}</ConnectedRouter>
73+
<Provider store={store} {...providers}>
74+
<ConnectedRouter history={history}>
75+
<ReduxAsyncConnect routes={_routes} store={store} helpers={providers}>
76+
{renderRoutes(_routes)}
77+
</ReduxAsyncConnect>
78+
</ConnectedRouter>
8379
</Provider>
8480
</HotEnabler>,
85-
dest,
81+
dest
8682
);
8783
};
8884

89-
await Loadable.preloadReady().then(hydrated => console.log('HYDRATED', hydrated));
85+
await Loadable.preloadReady();
9086

91-
hydrate(routes);
87+
await hydrate(routes);
9288

9389
if (module.hot) {
9490
module.hot.accept('./routes', () => {
9591
const nextRoutes = require('./routes');
96-
hydrate(nextRoutes);
92+
hydrate(nextRoutes).catch(err => {
93+
console.error('Error on routes reload:', err);
94+
});
9795
});
9896
}
9997

@@ -102,7 +100,7 @@ global.socket = initSocket();
102100

103101
if (!dest || !dest.firstChild || !dest.firstChild.attributes || !dest.firstChild.attributes['data-reactroot']) {
104102
console.error('Server-side React render was discarded.\n' +
105-
'Make sure that your initial render does not contain any client-side code.');
103+
'Make sure that your initial render does not contain any client-side code.');
106104
}
107105
}
108106

@@ -114,7 +112,7 @@ global.socket = initSocket();
114112
<Provider store={store}>
115113
<DevTools />
116114
</Provider>,
117-
devToolsDest,
115+
devToolsDest
118116
);
119117
}
120118

0 commit comments

Comments
 (0)