Skip to content
This repository was archived by the owner on Oct 26, 2018. It is now read-only.

Commit 02a41bf

Browse files
committed
Use ES6 import/export
1 parent 8422f9f commit 02a41bf

File tree

8 files changed

+325
-142
lines changed

8 files changed

+325
-142
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,22 +72,24 @@ it, and also change it with an action.
7272
Here's some code:
7373

7474
```js
75+
import React from 'react'
76+
import ReactDOM from 'react-dom'
7577
import { createStore, combineReducers } from 'redux'
7678
import { Provider } from 'react-redux'
7779
import { Router, Route } from 'react-router'
78-
import createBrowserHistory from 'history/lib/createBrowserHistory'
80+
import { createHistory } from 'history'
7981
import { syncReduxAndRouter, routeReducer } from 'redux-simple-router'
8082
import reducers from '<project-path>/reducers'
8183

8284
const reducer = combineReducers(Object.assign({}, reducers, {
8385
routing: routeReducer
8486
}))
8587
const store = createStore(reducer)
86-
const history = createBrowserHistory()
88+
const history = createHistory()
8789

8890
syncReduxAndRouter(history, store)
8991

90-
React.render(
92+
ReactDOM.render(
9193
<Provider store={store}>
9294
<Router history={history}>
9395
<Route path="/" component={App}>

examples/basic/app.js

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,41 @@
11
const React = require('react');
22
const ReactDOM = require('react-dom');
3-
const { createStore, combineReducers } = require('redux');
3+
const { compose, createStore, combineReducers } = require('redux');
44
const { Provider } = require('react-redux');
55
const { Router, Route, IndexRoute } = require('react-router');
66
const createHistory = require('history/lib/createHashHistory');
77
const { syncReduxAndRouter, routeReducer } = require('redux-simple-router');
8+
import { devTools } from 'redux-devtools';
9+
const { DevTools, DebugPanel, LogMonitor } = require('redux-devtools/lib/react');
810

911
const reducers = require('./reducers');
1012
const { App, Home, Foo, Bar } = require('./components');
1113

1214
const reducer = combineReducers(Object.assign({}, reducers, {
1315
routing: routeReducer
1416
}));
15-
const store = createStore(reducer);
17+
const finalCreateStore = compose(
18+
devTools()
19+
)(createStore);
20+
const store = finalCreateStore(reducer);
1621
const history = createHistory();
1722

1823
syncReduxAndRouter(history, store);
1924

2025
ReactDOM.render(
2126
<Provider store={store}>
22-
<Router history={history}>
23-
<Route path="/" component={App}>
24-
<IndexRoute component={Home}/>
25-
<Route path="foo" component={Foo}/>
26-
<Route path="bar" component={Bar}/>
27-
</Route>
28-
</Router>
27+
<div>
28+
<Router history={history}>
29+
<Route path="/" component={App}>
30+
<IndexRoute component={Home}/>
31+
<Route path="foo" component={Foo}/>
32+
<Route path="bar" component={Bar}/>
33+
</Route>
34+
</Router>
35+
<DebugPanel top right bottom>
36+
<DevTools store={store} monitor={LogMonitor} />
37+
</DebugPanel>
38+
</div>
2939
</Provider>,
3040
document.getElementById('mount')
3141
);

examples/basic/components/App.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
const React = require('react');
22
const { Link } = require('react-router');
33
const { connect } = require('react-redux');
4-
const { updatePath } = require('redux-simple-router');
4+
const { pushPath } = require('redux-simple-router');
55

6-
function App({ updatePath, children }) {
6+
function App({ pushPath, children }) {
77
return (
88
<div>
99
<header>
@@ -16,7 +16,7 @@ function App({ updatePath, children }) {
1616
<Link to="/bar">Bar</Link>
1717
</header>
1818
<div>
19-
<button onClick={() => updatePath('/foo')}>Go to /foo</button>
19+
<button onClick={() => pushPath('/foo')}>Go to /foo</button>
2020
</div>
2121
<div style={{marginTop: '1.5em'}}>{children}</div>
2222
</div>
@@ -25,5 +25,5 @@ function App({ updatePath, children }) {
2525

2626
module.exports = connect(
2727
null,
28-
{ updatePath }
28+
{ pushPath }
2929
)(App);

examples/basic/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
"babel-loader": "^6.2.0",
1616
"babel-preset-es2015": "^6.1.18",
1717
"babel-preset-react": "^6.1.18",
18+
"redux-devtools": "^2.1.5",
1819
"webpack": "^1.12.6"
20+
},
21+
"scripts": {
22+
"start": "webpack --watch"
1923
}
2024
}

examples/basic/webpack.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,9 @@ var fs = require('fs')
2626
if (fs.existsSync(src)) {
2727
// Use the latest src
2828
module.exports.resolve = { alias: { 'redux-simple-router': src } }
29+
module.exports.module.loaders.push({
30+
test: /\.js$/,
31+
loaders: ['babel'],
32+
include: src
33+
});
2934
}

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "0.0.10",
44
"description": "Ruthlessly simple bindings to keep react-router and redux in sync",
55
"main": "lib/index",
6+
"jsnext:main": "src/index",
67
"repository": {
78
"type": "git",
89
"url": "https://github.com/jlongster/redux-simple-router.git"
@@ -53,6 +54,7 @@
5354
"karma-webpack": "^1.7.0",
5455
"mocha": "^2.3.4",
5556
"redux": "^3.0.4",
57+
"redux-devtools": "^2.1.5",
5658
"webpack": "^1.12.9"
5759
},
5860
"dependencies": {

src/index.js

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
1-
const deepEqual = require('deep-equal');
1+
import deepEqual from 'deep-equal';
22

33
// Constants
44

5-
const UPDATE_PATH = "@@router/UPDATE_PATH";
5+
export const UPDATE_PATH = "@@router/UPDATE_PATH";
6+
const INIT_PATH = "@@router/INIT_PATH";
67
const SELECT_STATE = state => state.routing;
78

8-
// Action creator
9+
// Action creators
910

10-
function pushPath(path, state, { avoidRouterUpdate = false } = {}) {
11+
function initPath(path, state) {
12+
return {
13+
type: INIT_PATH,
14+
payload: {
15+
path: path,
16+
state: state,
17+
replace: false,
18+
avoidRouterUpdate: true
19+
}
20+
};
21+
}
22+
23+
export function pushPath(path, state, { avoidRouterUpdate = false } = {}) {
1124
return {
1225
type: UPDATE_PATH,
1326
payload: {
@@ -19,7 +32,7 @@ function pushPath(path, state, { avoidRouterUpdate = false } = {}) {
1932
};
2033
}
2134

22-
function replacePath(path, state, { avoidRouterUpdate = false } = {}) {
35+
export function replacePath(path, state, { avoidRouterUpdate = false } = {}) {
2336
return {
2437
type: UPDATE_PATH,
2538
payload: {
@@ -33,15 +46,15 @@ function replacePath(path, state, { avoidRouterUpdate = false } = {}) {
3346

3447
// Reducer
3548

36-
const initialState = {
49+
let initialState = {
3750
changeId: 1,
3851
path: undefined,
3952
state: undefined,
4053
replace: false
4154
};
4255

4356
function update(state=initialState, { type, payload }) {
44-
if(type === UPDATE_PATH) {
57+
if(type === INIT_PATH || type === UPDATE_PATH) {
4558
return Object.assign({}, state, {
4659
path: payload.path,
4760
changeId: state.changeId + (payload.avoidRouterUpdate ? 0 : 1),
@@ -55,12 +68,20 @@ function update(state=initialState, { type, payload }) {
5568
// Syncing
5669

5770
function locationsAreEqual(a, b) {
58-
return a.path === b.path && deepEqual(a.state, b.state);
71+
return a != null && b != null && a.path === b.path && deepEqual(a.state, b.state);
5972
}
6073

61-
function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) {
74+
export function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) {
6275
const getRouterState = () => selectRouterState(store.getState());
63-
let lastChangeId = 0;
76+
77+
// To properly handle store updates we need to track the last route.
78+
// This route contains a `changeId` which is updated on every
79+
// `pushPath` and `replacePath`. If this id changes we always
80+
// trigger a history update. However, if the id does not change, we
81+
// check if the location has changed, and if it is we trigger a
82+
// history update. It's possible for this to happen when something
83+
// reloads the entire app state such as redux devtools.
84+
let lastRoute = undefined;
6485

6586
if(!getRouterState()) {
6687
throw new Error(
@@ -75,30 +96,50 @@ function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) {
7596
state: location.state
7697
};
7798

78-
// Avoid dispatching an action if the store is already up-to-date,
79-
// even if `history` wouldn't do anything if the location is the same
80-
if(locationsAreEqual(getRouterState(), route)) return;
81-
82-
const updatePath = location.action === 'REPLACE'
83-
? replacePath
84-
: pushPath;
85-
86-
store.dispatch(updatePath(route.path, route.state, { avoidRouterUpdate: true }));
99+
if (!lastRoute) {
100+
// `initialState` *should* represent the current location when
101+
// the app loads, but we cannot get the current location when it
102+
// is defined. What happens is `history.listen` is called
103+
// immediately when it is registered, and it updates the app
104+
// state with an UPDATE_PATH action. This causes problem when
105+
// users are listening to UPDATE_PATH actions just for
106+
// *changes*, and with redux devtools because "revert" will use
107+
// `initialState` and it won't revert to the original URL.
108+
// Instead, we specialize the first route notification and do
109+
// different things based on it.
110+
initialState = {
111+
changeId: 1,
112+
path: route.path,
113+
state: route.state,
114+
replace: false
115+
};
116+
117+
// Also set `lastRoute` so that the store subscriber doesn't
118+
// trigger an unnecessary `pushState` on load
119+
lastRoute = initialState;
120+
121+
store.dispatch(initPath(route.path, route.state));
122+
} else if(!locationsAreEqual(getRouterState(), route)) {
123+
// The above check avoids dispatching an action if the store is
124+
// already up-to-date
125+
const method = location.action === 'REPLACE' ? replacePath : pushPath;
126+
store.dispatch(method(route.path, route.state, { avoidRouterUpdate: true }));
127+
}
87128
});
88129

89130
const unsubscribeStore = store.subscribe(() => {
90-
const routing = getRouterState();
91-
92-
// Only update the router once per `pushPath` call. This is
93-
// indicated by the `changeId` state; when that number changes, we
94-
// should update the history.
95-
if(lastChangeId === routing.changeId) return;
131+
let routing = getRouterState();
96132

97-
lastChangeId = routing.changeId;
133+
// Only trigger history update if this is a new change or the
134+
// location has changed.
135+
if(lastRoute.changeId !== routing.changeId ||
136+
!locationsAreEqual(lastRoute, routing)) {
98137

99-
const method = routing.replace ? 'replaceState' : 'pushState';
138+
lastRoute = routing;
139+
const method = routing.replace ? 'replaceState' : 'pushState';
140+
history[method](routing.state, routing.path);
141+
}
100142

101-
history[method](routing.state, routing.path);
102143
});
103144

104145
return function unsubscribe() {
@@ -107,10 +148,4 @@ function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) {
107148
};
108149
}
109150

110-
module.exports = {
111-
UPDATE_PATH,
112-
pushPath,
113-
replacePath,
114-
syncReduxAndRouter,
115-
routeReducer: update
116-
};
151+
export { update as routeReducer };

0 commit comments

Comments
 (0)