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

Following feedback in #259 #262

Merged
merged 4 commits into from
Feb 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 12 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ View the [CHANGELOG](https://github.com/rackt/react-router-redux/blob/master/CHA

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

**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:
**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:

### Usage

Expand All @@ -44,25 +44,25 @@ import ReactDOM from 'react-dom'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import { Router, Route, browserHistory } from 'react-router'
import { syncHistory, routeReducer } from 'react-router-redux'
import { syncHistoryWithStore, routerReducer } from 'react-router-redux'

import reducers from '<project-path>/reducers'

const reducer = combineReducers(Object.assign({}, reducers, {
routing: routeReducer
routing: routerReducer
}))

// Sync dispatched route actions to the history
const reduxRouterMiddleware = syncHistory(browserHistory)
const createStoreWithMiddleware = applyMiddleware(reduxRouterMiddleware)(createStore)
const store = createStore(reducer)

const store = createStoreWithMiddleware(reducer)
// Sync dispatched route actions to the history
const history = syncHistoryWithStore(browserHistory, store)

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

ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
<Router history={history}>
<Route path="/" component={App}>
<Route path="foo" component={Foo}/>
<Route path="bar" component={Bar}/>
Expand All @@ -73,7 +73,7 @@ ReactDOM.render(
)
```

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:
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:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User are discourage from reading state at all. I'm thinking of prefixing this key with a $ to make that clear. But this section of the README should actually go away or be significantly changed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the solution for users that refuse to define routes in jsx within a react component? Where should a user parse the location so they can get things like named path parameters using a tool like url-pattern?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Router hasn't had named routes for a while now. You should be using URLs or location descriptors for all of your navigation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to make a simple example that does not use react-router. How do I gain access to the location.pathname?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Listen to the history. It's a synchronous operation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that the point of this library? To listen to history and put it in the same place where every other component reads state from?


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

### API

#### `syncHistory(history: History) => ReduxMiddleware`

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.

#### `ReduxMiddleware.listenForReplays(store: ReduxStore, selectLocationState?: function)`

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.

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.

#### `ReduxMiddleware.unsubscribe()`
#### `history = syncHistoryWithStore(history: History, store)`

Call this on the middleware returned from `syncHistory` to stop the syncing process set up by `listenForReplays`.
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.

#### `routeReducer`
#### `routerReducer`

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).

Expand Down
24 changes: 16 additions & 8 deletions src/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ export default function syncHistoryWithStore(history, store, {
// Update address bar to reflect store state
isTimeTraveling = true
currentLocation = locationInStore
history.transitionTo(Object.assign({},
locationInStore,
{ action: 'PUSH' }
))
history.transitionTo({
...locationInStore,
action: 'PUSH'
})
isTimeTraveling = false
}

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

// The enhanced history uses store as source of truth
return Object.assign({}, history, {
return {
...history,
// The listeners are subscribed to the store instead of history
listen(listener) {
// Copy of last location.
let lastPublishedLocation = getLocationInStore(true)
// History listeners expect a synchronous call
listener(getLocationInStore(true))
listener(lastPublishedLocation)

// Keep track of whether we unsubscribed, as Redux store
// only applies changes in subscriptions on next dispatch
let unsubscribed = false
const unsubscribeFromStore = store.subscribe(() => {
const currentLocation = getLocationInStore(true)
if (currentLocation === lastPublishedLocation) {
return
}
lastPublishedLocation = currentLocation
if (!unsubscribed) {
listener(getLocationInStore(true))
listener(lastPublishedLocation)
}
})

Expand All @@ -124,5 +132,5 @@ export default function syncHistoryWithStore(history, store, {
}
unsubscribeFromHistory()
}
})
}
}