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

Commit 08b3818

Browse files
committed
Merge pull request #262 from webmasterkai/synchronicity
Following feedback in #259
2 parents 4c17b0e + 1a8c800 commit 08b3818

File tree

2 files changed

+28
-30
lines changed

2 files changed

+28
-30
lines changed

README.md

+12-22
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ View the [CHANGELOG](https://github.com/rackt/react-router-redux/blob/master/CHA
2222

2323
Read the [API docs](#api) farther down this page.
2424

25-
**Note:** We are [currently discussing some major changes](https://github.com/rackt/react-router-redux/issues/257) to the library. [React Router's API in 2.0](https://github.com/rackt/react-router/blob/master/upgrade-guides/v2.0.0.md) is significantly improved and obseletes the need for things like action creators and reading location state from the Redux. This library is still critical to enable things like time traveling and persisting state, so we're not going anywhere. But in many cases, you may not need this library and can simply use the provided React Router APIs. Go check them out and drop some technical debt. :smile:
25+
**Note:** We are [currently discussing some major changes](https://github.com/rackt/react-router-redux/issues/257) to the library. [React Router's API in 2.0](https://github.com/rackt/react-router/blob/master/upgrade-guides/v2.0.0.md) is significantly improved and makes things like action creators and reading location state from Redux obsolete. This library is still critical to enable things like time traveling and persisting state, so we're not going anywhere. But in many cases, you may not need this library and can simply use the provided React Router APIs. Go check them out and drop some technical debt. :smile:
2626

2727
### Usage
2828

@@ -44,25 +44,25 @@ import ReactDOM from 'react-dom'
4444
import { createStore, combineReducers, applyMiddleware } from 'redux'
4545
import { Provider } from 'react-redux'
4646
import { Router, Route, browserHistory } from 'react-router'
47-
import { syncHistory, routeReducer } from 'react-router-redux'
47+
import { syncHistoryWithStore, routerReducer } from 'react-router-redux'
48+
4849
import reducers from '<project-path>/reducers'
4950

5051
const reducer = combineReducers(Object.assign({}, reducers, {
51-
routing: routeReducer
52+
routing: routerReducer
5253
}))
5354

54-
// Sync dispatched route actions to the history
55-
const reduxRouterMiddleware = syncHistory(browserHistory)
56-
const createStoreWithMiddleware = applyMiddleware(reduxRouterMiddleware)(createStore)
55+
const store = createStore(reducer)
5756

58-
const store = createStoreWithMiddleware(reducer)
57+
// Sync dispatched route actions to the history
58+
const history = syncHistoryWithStore(browserHistory, store)
5959

6060
// Required for replaying actions from devtools to work
6161
reduxRouterMiddleware.listenForReplays(store)
6262

6363
ReactDOM.render(
6464
<Provider store={store}>
65-
<Router history={browserHistory}>
65+
<Router history={history}>
6666
<Route path="/" component={App}>
6767
<Route path="foo" component={Foo}/>
6868
<Route path="bar" component={Bar}/>
@@ -73,7 +73,7 @@ ReactDOM.render(
7373
)
7474
```
7575

76-
Now you can read from `state.routing.location.pathname` to get the URL. It's far more likely that you want to change the URL more often, however. You can use the `push` action creator that we provide:
76+
Now you can read from `state.routing.locationBeforeTransitions.pathname` to get the URL. It's far more likely that you want to change the URL more often, however. You can use the `push` action creator that we provide:
7777

7878
```js
7979
import { routeActions } from 'react-router-redux'
@@ -132,21 +132,11 @@ _Have an example to add? Send us a PR!_
132132

133133
### API
134134

135-
#### `syncHistory(history: History) => ReduxMiddleware`
136-
137-
Call this to create a middleware that can be applied with Redux's `applyMiddleware` to allow actions to call history methods. The middleware will look for route actions created by `push`, `replace`, etc. and applies them to the history.
138-
139-
#### `ReduxMiddleware.listenForReplays(store: ReduxStore, selectLocationState?: function)`
140-
141-
By default, the syncing logic will not respond to replaying of actions, which means it won't work with projects like redux-devtools. Call this function on the middleware object returned from `syncHistory` and give it the store to listen to, and it will properly work with action replays. Obviously, you would do that after you have created the store and everything else has been set up.
142-
143-
Supply an optional function `selectLocationState` to customize where to find the location state on your app state. It defaults to `state => state.routing.location`, so you would install the reducer under the name "routing". Feel free to change this to whatever you like.
144-
145-
#### `ReduxMiddleware.unsubscribe()`
135+
#### `history = syncHistoryWithStore(history: History, store)`
146136

147-
Call this on the middleware returned from `syncHistory` to stop the syncing process set up by `listenForReplays`.
137+
We now sync by enhancing the history instance to listen for navigation events and dispatch those into the store. The enhanced history has its listen method overridden to respond to store changes, rather than directly to navigation events. When this history is provided to <Router>, the router will listen to it and receive these store changes. This means if we time travel with the store, the router will receive those store changes and update based on the location in the store, instead of what the browser says. Normal navigation events (hitting your browser back/forward buttons, telling a history singleton to push a location) flow through the history's listener like normal, so all the usual stuff works A-OK.
148138

149-
#### `routeReducer`
139+
#### `routerReducer`
150140

151141
A reducer function that keeps track of the router state. You must add this reducer to your app reducers when creating the store. It will return a `location` property in state. If you use `combineReducers`, it will be nested under wherever property you add it to (`state.routing` in the example above).
152142

src/sync.js

+16-8
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ export default function syncHistoryWithStore(history, store, {
5454
// Update address bar to reflect store state
5555
isTimeTraveling = true
5656
currentLocation = locationInStore
57-
history.transitionTo(Object.assign({},
58-
locationInStore,
59-
{ action: 'PUSH' }
60-
))
57+
history.transitionTo({
58+
...locationInStore,
59+
action: 'PUSH'
60+
})
6161
isTimeTraveling = false
6262
}
6363

@@ -95,18 +95,26 @@ export default function syncHistoryWithStore(history, store, {
9595
unsubscribeFromHistory = history.listen(handleLocationChange)
9696

9797
// The enhanced history uses store as source of truth
98-
return Object.assign({}, history, {
98+
return {
99+
...history,
99100
// The listeners are subscribed to the store instead of history
100101
listen(listener) {
102+
// Copy of last location.
103+
let lastPublishedLocation = getLocationInStore(true)
101104
// History listeners expect a synchronous call
102-
listener(getLocationInStore(true))
105+
listener(lastPublishedLocation)
103106

104107
// Keep track of whether we unsubscribed, as Redux store
105108
// only applies changes in subscriptions on next dispatch
106109
let unsubscribed = false
107110
const unsubscribeFromStore = store.subscribe(() => {
111+
const currentLocation = getLocationInStore(true)
112+
if (currentLocation === lastPublishedLocation) {
113+
return
114+
}
115+
lastPublishedLocation = currentLocation
108116
if (!unsubscribed) {
109-
listener(getLocationInStore(true))
117+
listener(lastPublishedLocation)
110118
}
111119
})
112120

@@ -124,5 +132,5 @@ export default function syncHistoryWithStore(history, store, {
124132
}
125133
unsubscribeFromHistory()
126134
}
127-
})
135+
}
128136
}

0 commit comments

Comments
 (0)