Created
December 4, 2015 19:43
-
-
Save fkrauthan/0fe48db6891a15ea451c to your computer and use it in GitHub Desktop.
Why don't we just do this?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const deepEqual = require('deep-equal'); | |
// Constants | |
const UPDATE_PATH = "@@router/UPDATE_PATH"; | |
const SELECT_STATE = state => state.routing; | |
// Action creator | |
function pushPath(path, state, { avoidRouterUpdate = false } = {}) { | |
return { | |
type: UPDATE_PATH, | |
payload: { | |
path: path, | |
state: state, | |
replace: false, | |
avoidRouterUpdate: !!avoidRouterUpdate | |
} | |
}; | |
} | |
function replacePath(path, state, { avoidRouterUpdate = false } = {}) { | |
return { | |
type: UPDATE_PATH, | |
payload: { | |
path: path, | |
state: state, | |
replace: true, | |
avoidRouterUpdate: !!avoidRouterUpdate | |
} | |
} | |
} | |
// Reducer | |
const initialState = { | |
changeId: 1, | |
path: undefined, | |
state: undefined, | |
replace: false | |
}; | |
function update(state=initialState, { type, payload }) { | |
if(type === UPDATE_PATH) { | |
return Object.assign({}, state, { | |
path: payload.path, | |
changeId: state.changeId + (payload.avoidRouterUpdate ? 0 : 1), | |
state: payload.state, | |
replace: payload.replace | |
}); | |
} | |
return state; | |
} | |
// Syncing | |
function locationsAreEqual(a, b) { | |
return a.path === b.path && deepEqual(a.state, b.state); | |
} | |
function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) { | |
const getRouterState = () => selectRouterState(store.getState()); | |
let lastChangeId = 0; | |
if(!getRouterState()) { | |
throw new Error( | |
"Cannot sync router: route state does not exist. Did you " + | |
"install the routing reducer?" | |
); | |
} | |
let avoidAvoidRouterUpdate = false; | |
const unsubscribeHistory = history.listen(location => { | |
const route = { | |
path: history.createPath(location), | |
state: location.state | |
}; | |
// Avoid dispatching an action if the store is already up-to-date, | |
// even if `history` wouldn't do anything if the location is the same | |
if(locationsAreEqual(getRouterState(), route)) return; | |
const updatePath = location.action === 'REPLACE' | |
? replacePath | |
: pushPath; | |
avoidAvoidRouterUpdate = true; | |
store.dispatch(updatePath(route.path, route.state)); | |
}); | |
const unsubscribeStore = store.subscribe(() => { | |
const routing = getRouterState(); | |
// Only update the router once per `pushPath` call. This is | |
// indicated by the `changeId` state; when that number changes, we | |
// should update the history. | |
if(lastChangeId === routing.changeId) return; | |
lastChangeId = routing.changeId; | |
if(avoidAvoidRouterUpdate) { | |
avoidAvoidRouterUpdate = false; | |
return; | |
} | |
const method = routing.replace ? 'replaceState' : 'pushState'; | |
history[method](routing.state, routing.path); | |
}); | |
return function unsubscribe() { | |
unsubscribeHistory(); | |
unsubscribeStore(); | |
}; | |
} | |
module.exports = { | |
UPDATE_PATH, | |
pushPath, | |
replacePath, | |
syncReduxAndRouter, | |
routeReducer: update | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment