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 , noRouterUpdate ) {
10
+ function updatePath ( path , state , noRouterUpdate ) {
10
11
return {
11
12
type : UPDATE_PATH ,
12
13
path : path ,
13
- noRouterUpdate : noRouterUpdate
14
+ replace : false ,
15
+ noRouterUpdate : ! ! noRouterUpdate
16
+ }
17
+ }
18
+
19
+ function replacePath ( path , state , noRouterUpdate ) {
20
+ return {
21
+ type : UPDATE_PATH ,
22
+ path : path ,
23
+ replace : true ,
24
+ noRouterUpdate : ! ! noRouterUpdate
14
25
}
15
26
}
16
27
17
28
// Reducer
18
29
19
- const initialState = typeof window === 'undefined' ? { } : {
20
- path : locationToString ( window . location )
30
+ const initialState = {
31
+ path : undefined ,
32
+ state : undefined ,
33
+ replace : false ,
34
+ noRouterUpdate : true
21
35
} ;
22
36
23
37
function update ( state = initialState , action ) {
24
38
if ( action . type === UPDATE_PATH ) {
25
39
return Object . assign ( { } , state , {
26
40
path : action . path ,
41
+ state : action . state ,
42
+ replace : action . replace ,
27
43
noRouterUpdate : action . noRouterUpdate
28
44
} ) ;
29
45
}
@@ -32,8 +48,8 @@ function update(state=initialState, action) {
32
48
33
49
// Syncing
34
50
35
- function locationToString ( location ) {
36
- return location . pathname + location . search + location . hash ;
51
+ function locationsAreEqual ( a , b ) {
52
+ return a . path === b . path && deepEqual ( a . state , b . state ) ;
37
53
}
38
54
39
55
function syncReduxAndRouter ( history , store , selectRouterState = SELECT_STATE ) {
@@ -46,12 +62,24 @@ function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) {
46
62
) ;
47
63
}
48
64
65
+ let historyLocation = { } ;
66
+
49
67
const unsubscribeHistory = history . listen ( location => {
68
+ historyLocation = {
69
+ path : history . createPath ( location ) ,
70
+ state : location . state
71
+ } ;
72
+
73
+ const routing = getRouterState ( ) ;
74
+
50
75
// Avoid dispatching an action if the store is already up-to-date,
51
76
// even if `history` wouldn't do anything if the location is the same
52
- if ( getRouterState ( ) . path !== locationToString ( location ) ) {
53
- store . dispatch ( updatePath ( locationToString ( location ) ) ) ;
54
- }
77
+ if ( locationsAreEqual ( routing , historyLocation ) ) return ;
78
+
79
+ // Initial state, update store with the current location but skip routing
80
+ const noRouterUpdate = routing . path == null ;
81
+
82
+ store . dispatch ( updatePath ( historyLocation . path , historyLocation . state , noRouterUpdate ) ) ;
55
83
} ) ;
56
84
57
85
const unsubscribeStore = store . subscribe ( ( ) => {
@@ -61,9 +89,12 @@ function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) {
61
89
// `noRouterUpdate` flag can be set to avoid updating altogether,
62
90
// which is useful for things like loading snapshots or very special
63
91
// edge cases.
64
- if ( routing . path !== locationToString ( window . location ) &&
65
- ! routing . noRouterUpdate ) {
66
- history . pushState ( null , routing . path ) ;
92
+ if ( locationsAreEqual ( routing , historyLocation ) || routing . noRouterUpdate ) return ;
93
+
94
+ if ( routing . replace ) {
95
+ history . replaceState ( routing . state , routing . path ) ;
96
+ } else {
97
+ history . pushState ( routing . state , routing . path ) ;
67
98
}
68
99
} ) ;
69
100
@@ -76,6 +107,7 @@ function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) {
76
107
module . exports = {
77
108
UPDATE_PATH ,
78
109
updatePath,
110
+ replacePath,
79
111
syncReduxAndRouter,
80
112
routeReducer : update ,
81
113
} ;
0 commit comments