1+ const deepEqual = require ( 'deep-equal' ) ;
12
2- // constants
3+ // Constants
34
45const UPDATE_PATH = "@@router/UPDATE_PATH" ;
56const SELECT_STATE = state => state . routing ;
67
78// Action creator
89
9- function updatePath ( path , avoidRouterUpdate ) {
10+ function pushPath ( path , state , avoidRouterUpdate ) {
1011 return {
1112 type : UPDATE_PATH ,
1213 path : path ,
14+ state : state ,
15+ replace : false ,
16+ avoidRouterUpdate : ! ! avoidRouterUpdate
17+ } ;
18+ }
19+
20+ function replacePath ( path , state , avoidRouterUpdate ) {
21+ return {
22+ type : UPDATE_PATH ,
23+ path : path ,
24+ state : state ,
25+ replace : true ,
1326 avoidRouterUpdate : ! ! avoidRouterUpdate
1427 }
1528}
@@ -18,25 +31,27 @@ function updatePath(path, avoidRouterUpdate) {
1831
1932const initialState = {
2033 changeId : 1 ,
21- path : ( typeof window !== ' undefined' ) ?
22- locationToString ( window . location ) :
23- '/'
34+ path : undefined ,
35+ state : undefined ,
36+ replace : false
2437} ;
2538
2639function update ( state = initialState , action ) {
2740 if ( action . type === UPDATE_PATH ) {
2841 return Object . assign ( { } , state , {
2942 path : action . path ,
30- changeId : state . changeId + ( action . avoidRouterUpdate ? 0 : 1 )
43+ changeId : state . changeId + ( action . avoidRouterUpdate ? 0 : 1 ) ,
44+ state : action . state ,
45+ replace : action . replace
3146 } ) ;
3247 }
3348 return state ;
3449}
3550
3651// Syncing
3752
38- function locationToString ( location ) {
39- return location . pathname + location . search + location . hash ;
53+ function locationsAreEqual ( a , b ) {
54+ return a . path === b . path && deepEqual ( a . state , b . state ) ;
4055}
4156
4257function syncReduxAndRouter ( history , store , selectRouterState = SELECT_STATE ) {
@@ -50,25 +65,36 @@ function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) {
5065 ) ;
5166 }
5267
68+ let historyLocation = { } ;
69+
5370 const unsubscribeHistory = history . listen ( location => {
54- const routePath = locationToString ( location ) ;
71+ historyLocation = {
72+ path : history . createPath ( location ) ,
73+ state : location . state
74+ } ;
75+
76+ const routing = getRouterState ( ) ;
77+
78+ // Avoid dispatching an action if the store is already up-to-date,
79+ // even if `history` wouldn't do anything if the location is the same
80+ if ( locationsAreEqual ( routing , historyLocation ) ) return ;
5581
56- // Avoid dispatching an action if the store is already up-to-date
57- if ( getRouterState ( ) . path !== routePath ) {
58- store . dispatch ( updatePath ( routePath , { avoidRouterUpdate : true } ) ) ;
59- }
82+ store . dispatch ( pushPath ( historyLocation . path , historyLocation . state , { avoidRouterUpdate : true } ) ) ;
6083 } ) ;
6184
6285 const unsubscribeStore = store . subscribe ( ( ) => {
6386 const routing = getRouterState ( ) ;
6487
65- // Only update the router once per `updatePath ` call. This is
88+ // Only update the router once per `pushPath ` call. This is
6689 // indicated by the `changeId` state; when that number changes, we
67- // should call `pushState`.
68- if ( lastChangeId !== routing . changeId ) {
69- lastChangeId = routing . changeId ;
70- history . pushState ( null , routing . path ) ;
71- }
90+ // should update the history.
91+ if ( lastChangeId === routing . changeId ) return ;
92+
93+ lastChangeId = routing . changeId ;
94+
95+ const method = routing . replace ? 'replaceState' : 'pushState' ;
96+
97+ history [ method ] ( routing . state , routing . path ) ;
7298 } ) ;
7399
74100 return function unsubscribe ( ) {
@@ -79,7 +105,8 @@ function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) {
79105
80106module . exports = {
81107 UPDATE_PATH ,
82- updatePath,
108+ pushPath,
109+ replacePath,
83110 syncReduxAndRouter,
84111 routeReducer : update
85112} ;
0 commit comments