1- import deepEqual from 'deep-equal'
1+ import { createMemoryHistory } from 'history' ;
22
33// Constants
44
55export const UPDATE_PATH = '@@router/UPDATE_PATH'
66const SELECT_STATE = state => state . routing
77
8- export function pushPath ( path , state , { avoidRouterUpdate = false } = { } ) {
8+ export function pushPath ( path , state ) {
99 return {
1010 type : UPDATE_PATH ,
1111 payload : {
1212 path : path ,
1313 state : state ,
1414 replace : false ,
15- avoidRouterUpdate : ! ! avoidRouterUpdate
1615 }
1716 }
1817}
1918
20- export function replacePath ( path , state , { avoidRouterUpdate = false } = { } ) {
19+ export function replacePath ( path , state ) {
2120 return {
2221 type : UPDATE_PATH ,
2322 payload : {
2423 path : path ,
2524 state : state ,
2625 replace : true ,
27- avoidRouterUpdate : ! ! avoidRouterUpdate
2826 }
2927 }
3028}
@@ -42,7 +40,7 @@ function update(state=initialState, { type, payload }) {
4240 if ( type === UPDATE_PATH ) {
4341 return Object . assign ( { } , state , {
4442 path : payload . path ,
45- changeId : state . changeId + ( payload . avoidRouterUpdate ? 0 : 1 ) ,
43+ changeId : state . changeId + 1 ,
4644 state : payload . state ,
4745 replace : payload . replace
4846 } )
@@ -52,10 +50,6 @@ function update(state=initialState, { type, payload }) {
5250
5351// Syncing
5452
55- function locationsAreEqual ( a , b ) {
56- return a != null && b != null && a . path === b . path && deepEqual ( a . state , b . state )
57- }
58-
5953function createPath ( location ) {
6054 const { pathname, search, hash } = location
6155 let result = pathname
@@ -66,32 +60,53 @@ function createPath(location) {
6660 return result
6761}
6862
69- export function syncReduxAndRouter ( history , store , selectRouterState = SELECT_STATE ) {
63+ export function syncReduxAndRouter ( browserHistory , store , selectRouterState = SELECT_STATE ) {
7064 const getRouterState = ( ) => selectRouterState ( store . getState ( ) )
71-
72- // To properly handle store updates we need to track the last route.
73- // This route contains a `changeId` which is updated on every
74- // `pushPath` and `replacePath`. If this id changes we always
75- // trigger a history update. However, if the id does not change, we
76- // check if the location has changed, and if it is we trigger a
77- // history update. It's possible for this to happen when something
78- // reloads the entire app state such as redux devtools.
79- let lastRoute = undefined
80-
8165 if ( ! getRouterState ( ) ) {
8266 throw new Error (
8367 'Cannot sync router: route state does not exist (`state.routing` by default). ' +
8468 'Did you install the routing reducer?'
8569 )
8670 }
8771
88- const unsubscribeHistory = history . listen ( location => {
72+ const memoryHistory = createMemoryHistory ( ) ;
73+
74+ let lastId = null ;
75+ let storeListeningToBrowser = true ;
76+ let browserListeningToStore = true ;
77+
78+ const unsubscribeStore = store . subscribe ( ( ) => {
79+ const routing = getRouterState ( ) ;
80+
81+ if ( lastId !== routing . changeId ) {
82+ lastId = routing . changeId ;
83+
84+ const method = routing . replace ? 'replace' : 'push'
85+ console . log ( "sending to memoryHistory from store subscriber" ) ;
86+ memoryHistory [ method ] ( {
87+ pathname : routing . path ,
88+ state : routing . state
89+ } ) ;
90+
91+ if ( browserListeningToStore ) {
92+ console . log ( "pushing to browserHistory from store subscriber" ) ;
93+ storeListeningToBrowser = false ;
94+ browserHistory [ method ] ( {
95+ pathname : routing . path ,
96+ state : routing . state
97+ } ) ;
98+ storeListeningToBrowser = true ;
99+ }
100+ }
101+ } ) ;
102+
103+ const unsubscribeHistory = browserHistory . listen ( ( location ) => {
89104 const route = {
90105 path : createPath ( location ) ,
91106 state : location . state
92107 }
93108
94- if ( ! lastRoute ) {
109+ if ( ! lastId ) {
95110 // `initialState` *should* represent the current location when
96111 // the app loads, but we cannot get the current location when it
97112 // is defined. What happens is `history.listen` is called
@@ -108,42 +123,30 @@ export function syncReduxAndRouter(history, store, selectRouterState = SELECT_ST
108123 state : route . state ,
109124 replace : false
110125 }
111-
112- // Also set `lastRoute` so that the store subscriber doesn't
113- // trigger an unnecessary `pushState` on load
114- lastRoute = initialState
115-
116- store . dispatch ( pushPath ( route . path , route . state , { avoidRouterUpdate : true } ) ) ;
117- } else if ( ! locationsAreEqual ( getRouterState ( ) , route ) ) {
118- // The above check avoids dispatching an action if the store is
119- // already up-to-date
126+ }
127+ if ( storeListeningToBrowser ) {
128+ console . log ( "got new location in browserHistory listener, sending to store" )
129+ browserListeningToStore = false ;
120130 const method = location . action === 'REPLACE' ? replacePath : pushPath
121- store . dispatch ( method ( route . path , route . state , { avoidRouterUpdate : true } ) )
131+ store . dispatch ( method ( route . path , route . state ) ) ;
132+ browserListeningToStore = true ;
122133 }
123- } )
124-
125- const unsubscribeStore = store . subscribe ( ( ) => {
126- let routing = getRouterState ( )
127-
128- // Only trigger history update if this is a new change or the
129- // location has changed.
130- if ( lastRoute . changeId !== routing . changeId ||
131- ! locationsAreEqual ( lastRoute , routing ) ) {
132-
133- lastRoute = routing
134- const method = routing . replace ? 'replace' : 'push'
135- history [ method ] ( {
136- pathname : routing . path ,
137- state : routing . state
138- } )
134+ } ) ;
135+
136+ const reduxRouterHistory = Object . assign ( { } , memoryHistory , {
137+ unsubscribe : function ( ) {
138+ unsubscribeStore ( ) ;
139+ unsubscribeHistory ( ) ;
140+ } ,
141+ push : function ( location ) {
142+ console . log ( "Got push in reduxRouterHistory (i.e. <Link/>). sending to store" ) ;
143+ const method = location . action === 'REPLACE' ? replacePath : pushPath
144+ store . dispatch ( method ( createPath ( location ) , location . state ) ) ;
139145 }
146+ } ) ;
140147
141- } )
148+ return reduxRouterHistory ;
142149
143- return function unsubscribe ( ) {
144- unsubscribeHistory ( )
145- unsubscribeStore ( )
146- }
147150}
148151
149152export { update as routeReducer }
0 commit comments