@@ -2,10 +2,23 @@ const deepEqual = require('deep-equal');
2
2
3
3
// Constants
4
4
5
+ const INIT_PATH = "@@router/INIT_PATH" ;
5
6
const UPDATE_PATH = "@@router/UPDATE_PATH" ;
6
7
const SELECT_STATE = state => state . routing ;
7
8
8
- // Action creator
9
+ // Action creators
10
+
11
+ function initPath ( path , state ) {
12
+ return {
13
+ type : INIT_PATH ,
14
+ payload : {
15
+ path : path ,
16
+ state : state ,
17
+ replace : false ,
18
+ avoidRouterUpdate : true
19
+ }
20
+ } ;
21
+ }
9
22
10
23
function pushPath ( path , state , { avoidRouterUpdate = false } = { } ) {
11
24
return {
@@ -33,15 +46,15 @@ function replacePath(path, state, { avoidRouterUpdate = false } = {}) {
33
46
34
47
// Reducer
35
48
36
- const initialState = {
49
+ let initialState = {
37
50
changeId : 1 ,
38
51
path : undefined ,
39
52
state : undefined ,
40
53
replace : false
41
54
} ;
42
55
43
56
function update ( state = initialState , { type, payload } ) {
44
- if ( type === UPDATE_PATH ) {
57
+ if ( type === INIT_PATH || type === UPDATE_PATH ) {
45
58
return Object . assign ( { } , state , {
46
59
path : payload . path ,
47
60
changeId : state . changeId + ( payload . avoidRouterUpdate ? 0 : 1 ) ,
@@ -61,16 +74,6 @@ function locationsAreEqual(a, b) {
61
74
function syncReduxAndRouter ( history , store , selectRouterState = SELECT_STATE ) {
62
75
const getRouterState = ( ) => selectRouterState ( store . getState ( ) ) ;
63
76
64
- // `initialState` *sould* represent the current location when the
65
- // app loads, but we cannot get the current location when it is
66
- // defined. What happens is `history.listen` is called immediately
67
- // when it is registered, and it updates the app state with an
68
- // action. This causes problems with redux devtools because "revert"
69
- // will use `initialState` and it won't revert to the original URL.
70
- // Instead, we track the first route and hack it to load when using
71
- // the `initialState`.
72
- let firstRoute = undefined ;
73
-
74
77
// To properly handle store updates we need to track the last route.
75
78
// This route contains a `changeId` which is updated on every
76
79
// `pushPath` and `replacePath`. If this id changes we always
@@ -93,14 +96,32 @@ function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) {
93
96
state : location . state
94
97
} ;
95
98
96
- if ( firstRoute === undefined ) {
97
- firstRoute = route ;
98
- }
99
-
100
- // Avoid dispatching an action if the store is already up-to-date,
101
- // even if `history` wouldn't do anything if the location is the
102
- // same
103
- if ( ! locationsAreEqual ( getRouterState ( ) , route ) ) {
99
+ if ( ! lastRoute ) {
100
+ // `initialState` *should* represent the current location when
101
+ // the app loads, but we cannot get the current location when it
102
+ // is defined. What happens is `history.listen` is called
103
+ // immediately when it is registered, and it updates the app
104
+ // state with an UPDATE_PATH action. This causes problem when
105
+ // users are listening to UPDATE_PATH actions just for
106
+ // *changes*, and with redux devtools because "revert" will use
107
+ // `initialState` and it won't revert to the original URL.
108
+ // Instead, we specialize the first route notification and do
109
+ // different things based on it.
110
+ initialState = {
111
+ changeId : 1 ,
112
+ path : route . path ,
113
+ state : route . state ,
114
+ replace : false
115
+ } ;
116
+
117
+ // Also set `lastRoute` so that the store subscriber doesn't
118
+ // trigger an unnecessary `pushState` on load
119
+ lastRoute = initialState ;
120
+
121
+ store . dispatch ( initPath ( route . path , route . state ) ) ;
122
+ } else if ( ! locationsAreEqual ( getRouterState ( ) , route ) ) {
123
+ // The above check avoids dispatching an action if the store is
124
+ // already up-to-date
104
125
const method = location . action === 'REPLACE' ? replacePath : pushPath ;
105
126
store . dispatch ( method ( route . path , route . state , { avoidRouterUpdate : true } ) ) ;
106
127
}
@@ -109,15 +130,9 @@ function syncReduxAndRouter(history, store, selectRouterState = SELECT_STATE) {
109
130
const unsubscribeStore = store . subscribe ( ( ) => {
110
131
let routing = getRouterState ( ) ;
111
132
112
- // Treat `firstRoute` as our `initialState`
113
- if ( routing === initialState ) {
114
- routing = firstRoute ;
115
- }
116
-
117
- // Only trigger history update is this is a new change or the
133
+ // Only trigger history update if this is a new change or the
118
134
// location has changed.
119
- if ( lastRoute === undefined ||
120
- lastRoute . changeId !== routing . changeId ||
135
+ if ( lastRoute . changeId !== routing . changeId ||
121
136
! locationsAreEqual ( lastRoute , routing ) ) {
122
137
123
138
lastRoute = routing ;
0 commit comments