1
+ const deepEqual = require ( 'deep-equal' ) ;
1
2
2
- // constants
3
+ // Constants
3
4
4
5
const UPDATE_PATH = "@@router/UPDATE_PATH" ;
5
6
const SELECT_STATE = state => state . routing ;
6
7
7
8
// Action creator
8
9
9
- function updatePath ( path , avoidRouterUpdate ) {
10
+ function pushPath ( path , state , opts ) {
11
+ opts = opts || { } ;
10
12
return {
11
13
type : UPDATE_PATH ,
12
14
path : path ,
13
- avoidRouterUpdate : ! ! avoidRouterUpdate
15
+ state : state ,
16
+ replace : false ,
17
+ avoidRouterUpdate : ! ! opts . avoidRouterUpdate
18
+ } ;
19
+ }
20
+
21
+ function replacePath ( path , state , opts ) {
22
+ opts = opts || { } ;
23
+ return {
24
+ type : UPDATE_PATH ,
25
+ path : path ,
26
+ state : state ,
27
+ replace : true ,
28
+ avoidRouterUpdate : ! ! opts . avoidRouterUpdate
14
29
}
15
30
}
16
31
17
32
// Reducer
18
33
19
34
const initialState = {
20
35
changeId : 1 ,
21
- path : ( typeof window !== ' undefined' ) ?
22
- locationToString ( window . location ) :
23
- '/'
36
+ path : undefined ,
37
+ state : undefined ,
38
+ replace : false
24
39
} ;
25
40
26
41
function update ( state = initialState , action ) {
27
42
if ( action . type === UPDATE_PATH ) {
28
43
return Object . assign ( { } , state , {
29
44
path : action . path ,
30
- changeId : state . changeId + ( action . avoidRouterUpdate ? 0 : 1 )
45
+ changeId : state . changeId + ( action . avoidRouterUpdate ? 0 : 1 ) ,
46
+ state : action . state ,
47
+ replace : action . replace
31
48
} ) ;
32
49
}
33
50
return state ;
34
51
}
35
52
36
53
// Syncing
37
54
38
- function locationToString ( location ) {
39
- return location . pathname + location . search + location . hash ;
55
+ function locationsAreEqual ( a , b ) {
56
+ return a . path === b . path && deepEqual ( a . state , b . state ) ;
40
57
}
41
58
42
59
function syncReduxAndRouter ( history , store , selectRouterState = SELECT_STATE ) {
@@ -51,24 +68,31 @@ function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) {
51
68
}
52
69
53
70
const unsubscribeHistory = history . listen ( location => {
54
- const routePath = locationToString ( location ) ;
71
+ const route = {
72
+ path : history . createPath ( location ) ,
73
+ state : location . state
74
+ } ;
75
+
76
+ // Avoid dispatching an action if the store is already up-to-date,
77
+ // even if `history` wouldn't do anything if the location is the same
78
+ if ( locationsAreEqual ( getRouterState ( ) , route ) ) return ;
55
79
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
- }
80
+ store . dispatch ( pushPath ( route . path , route . state , { avoidRouterUpdate : true } ) ) ;
60
81
} ) ;
61
82
62
83
const unsubscribeStore = store . subscribe ( ( ) => {
63
84
const routing = getRouterState ( ) ;
64
85
65
- // Only update the router once per `updatePath ` call. This is
86
+ // Only update the router once per `pushPath ` call. This is
66
87
// 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
- }
88
+ // should update the history.
89
+ if ( lastChangeId === routing . changeId ) return ;
90
+
91
+ lastChangeId = routing . changeId ;
92
+
93
+ const method = routing . replace ? 'replaceState' : 'pushState' ;
94
+
95
+ history [ method ] ( routing . state , routing . path ) ;
72
96
} ) ;
73
97
74
98
return function unsubscribe ( ) {
@@ -79,7 +103,8 @@ function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) {
79
103
80
104
module . exports = {
81
105
UPDATE_PATH ,
82
- updatePath,
106
+ pushPath,
107
+ replacePath,
83
108
syncReduxAndRouter,
84
109
routeReducer : update
85
110
} ;
0 commit comments