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

[WIP] Tweak the API to handle initial state correctly #255

Closed
wants to merge 2 commits into from
Closed
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
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,18 @@ const reducer = combineReducers(Object.assign({}, reducers, {
routing: routeReducer
}))

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

const store = createStoreWithMiddleware(reducer)
const store = createStore(
reducer,
applyMiddleware(reduxRouterMiddleware)
)

// Required for replaying actions from devtools to work
reduxRouterMiddleware.listenForReplays(store)
// begin syncing
reduxRouterMiddleware.syncWith(store, {
urlToState: true, // route changes will appear in state
stateToUrl: false // set to true for time travel in DevTools
})

ReactDOM.render(
<Provider store={store}>
Expand Down Expand Up @@ -140,7 +144,7 @@ Examples from the community:

_Have an example to add? Send us a PR!_

### API
### API (TODO)

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

Expand Down
20 changes: 12 additions & 8 deletions examples/basic/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import DockMonitor from 'redux-devtools-dock-monitor'

import React from 'react'
import ReactDOM from 'react-dom'
import { applyMiddleware, compose, createStore, combineReducers } from 'redux'
import { compose, createStore, combineReducers } from 'redux'
import { Provider } from 'react-redux'
import { Router, Route, IndexRoute } from 'react-router'
import createHistory from 'history/lib/createHashHistory'
Expand All @@ -14,7 +14,10 @@ import * as reducers from './reducers'
import { App, Home, Foo, Bar } from './components'

const history = createHistory()
const middleware = syncHistory(history)
const enhancer = syncHistory(history, {
urlToState: true,
stateToUrl: true
})
const reducer = combineReducers({
...reducers,
routing: routeReducer
Expand All @@ -27,12 +30,13 @@ const DevTools = createDevTools(
</DockMonitor>
)

const finalCreateStore = compose(
applyMiddleware(middleware),
DevTools.instrument()
)(createStore)
const store = finalCreateStore(reducer)
middleware.listenForReplays(store)
const store = createStore(
reducer,
compose(
enhancer,
DevTools.instrument()
)
)

ReactDOM.render(
<Provider store={store}>
Expand Down
4 changes: 2 additions & 2 deletions examples/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"react-dom": "^0.14.2",
"react-redux": "^4.0.0",
"react-router": "^1.0.0",
"redux": "^3.0.4",
"react-router-redux": "^2.1.0"
"react-router-redux": "^2.1.0",
"redux": "^3.2.1"
},
"devDependencies": {
"babel-core": "^6.1.21",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"karma-webpack": "^1.7.0",
"mocha": "^2.3.4",
"react": "^0.14.3",
"redux": "^3.0.4",
"redux": "^3.2.1",
"redux-devtools": "^3.0.0",
"redux-devtools-dock-monitor": "^1.0.1",
"redux-devtools-log-monitor": "^1.0.1",
Expand Down
104 changes: 66 additions & 38 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,21 @@ export function routeReducer(state = initialState, { type, payload: location })

// Syncing

export function syncHistory(history) {
let unsubscribeHistory, currentKey, unsubscribeStore
let connected = false, syncing = false
export function syncHistory(history, {
urlToState = false,
stateToUrl = false,
selectLocationState = SELECT_LOCATION
} = {}) {
if (!urlToState && !stateToUrl) {
return createStore => createStore
}

history.listen(location => { initialState.location = location })()

function middleware(store) {
let currentKey, syncing = false
let unsubscribeStore, unsubscribeHistory

const updateStoreOnHistoryChange = (store) => {
unsubscribeHistory = history.listen(location => {
currentKey = location.key
if (syncing) {
Expand All @@ -59,54 +67,74 @@ export function syncHistory(history) {

store.dispatch(updateLocation(location))
})
}
const updateHistoryOnStoreChange = (store) => {
const getLocationState = () => selectLocationState(store.getState())
const initialLocation = getLocationState()

const reconcileLocationWithState = () => {
const location = getLocationState()

// If we're resetting to the beginning, use the saved initial value. We
// need to dispatch a new action at this point to populate the store
// appropriately.
if (location.key === initialLocation.key) {
history.replace(initialLocation)
return
}

connected = true
// Otherwise, if we need to update the history location, do so without
// dispatching a new action, as we're just bringing history in sync
// with the store.
if (location.key !== currentKey) {
syncing = true
history.transitionTo(location)
syncing = false
}
}

reconcileLocationWithState()
unsubscribeStore = store.subscribe(reconcileLocationWithState)
}

return next => action => {
if (action.type !== TRANSITION || !connected) {
return next(action)
const enhancer = createStore => (reducer, initialState, enhancer) => {
const store = createStore(reducer, initialState, enhancer)
const { dispatch: originalDispatch } = store

const dispatch = (action) => {
if (action.type !== TRANSITION || !unsubscribeHistory) {
return originalDispatch(action)
}

const { payload: { method, args } } = action
history[method](...args)
}
}

middleware.listenForReplays =
(store, selectLocationState = SELECT_LOCATION) => {
const getLocationState = () => selectLocationState(store.getState())
const initialLocation = getLocationState()

unsubscribeStore = store.subscribe(() => {
const location = getLocationState()

// If we're resetting to the beginning, use the saved initial value. We
// need to dispatch a new action at this point to populate the store
// appropriately.
if (location.key === initialLocation.key) {
history.replace(initialLocation)
return
}

// Otherwise, if we need to update the history location, do so without
// dispatching a new action, as we're just bringing history in sync
// with the store.
if (location.key !== currentKey) {
syncing = true
history.transitionTo(location)
syncing = false
}
})
if (stateToUrl) {
updateHistoryOnStoreChange(store)
}

if (urlToState) {
updateStoreOnHistoryChange(store)
}

middleware.unsubscribe = () => {
unsubscribeHistory()
return {
...store,
dispatch
}
}

enhancer.dispose = () => {
if (unsubscribeStore) {
unsubscribeStore()
unsubscribeStore = null
}

connected = false
if (unsubscribeHistory) {
unsubscribeHistory()
unsubscribeHistory = null
}
}

return middleware
return enhancer
}
Loading