1
1
import React , {
2
2
createContext ,
3
3
useEffect ,
4
+ useLayoutEffect ,
4
5
forwardRef ,
5
6
useImperativeHandle ,
6
7
ForwardedRef ,
7
8
} from 'react'
8
9
import { urlToPageKey , pathWithoutBZParams } from '../utils'
9
- import { removePage , historyChange , setActivePage } from '../actions'
10
+ import { removePage , setActivePage } from '../actions'
10
11
import {
11
12
HistoryState ,
12
13
RootState ,
@@ -15,7 +16,6 @@ import {
15
16
NavigationProviderProps ,
16
17
AllPages ,
17
18
SuperglueState ,
18
- PageKey ,
19
19
} from '../types'
20
20
import { Update } from 'history'
21
21
import { useDispatch , useSelector , useStore } from 'react-redux'
@@ -53,12 +53,23 @@ const NavigationProvider = forwardRef(function NavigationProvider(
53
53
const superglue = useSelector < RootState , SuperglueState > (
54
54
( state ) => state . superglue
55
55
)
56
+ const currentPageKey = useSelector < RootState , string > (
57
+ ( state ) => state . superglue . currentPageKey
58
+ )
56
59
const store = useStore ( )
57
60
58
61
useEffect ( ( ) => {
59
62
return history . listen ( onHistoryChange )
60
63
} , [ ] )
61
64
65
+ useLayoutEffect ( ( ) => {
66
+ const state = history . location . state as HistoryState
67
+ if ( state && 'superglue' in state ) {
68
+ const { posX, posY } = state
69
+ setWindowScroll ( posX , posY )
70
+ }
71
+ } , [ currentPageKey ] )
72
+
62
73
useImperativeHandle (
63
74
ref ,
64
75
( ) => {
@@ -69,36 +80,14 @@ const NavigationProvider = forwardRef(function NavigationProvider(
69
80
[ ]
70
81
)
71
82
72
- const visitAndRestore = ( pageKey : PageKey , posX : number , posY : number ) => {
73
- // When the application visit gets called with revisit: true
74
- // - In cases where the response was not redirected, the calculated
75
- // navigationAction is set to 'none' (meaning `navigateTo` immediately returned `false`)
76
- // and so we have restore scroll and the set the active page
77
- // - In cases where the response was redirected, the calculated
78
- // navigationAction is set to 'replace', and is handled gracefully by navigateTo,
79
- // before this method gets called.
80
- // That's why we're only concerned with the first case, but we gracefully warn
81
- // if the application visit did not return the meta object like the dev was supposed to.
82
- return visit ( pageKey , { revisit : true } ) . then ( ( meta ) => {
83
- if ( meta ) {
84
- if ( meta . navigationAction === 'none' ) {
85
- dispatch ( setActivePage ( { pageKey } ) )
86
- setWindowScroll ( posX , posY )
87
- }
88
- } else {
89
- console . warn (
90
- `scoll restoration was skipped. Your visit's then funtion
91
- should return the meta object it recieved if you want your
92
- application to restore the page's previous scroll.`
93
- )
94
- }
95
- } )
96
- }
97
-
98
83
const onHistoryChange = ( { location, action } : Update ) : void => {
99
84
const state = location . state as HistoryState
100
85
101
- if ( ! state && location . hash !== '' && action === 'POP' ) {
86
+ if ( action !== 'POP' ) {
87
+ return
88
+ }
89
+
90
+ if ( ! state && location . hash !== '' ) {
102
91
const nextPageKey = urlToPageKey ( location . pathname + location . search )
103
92
const containsKey = ! ! pages [ nextPageKey ]
104
93
if ( containsKey ) {
@@ -119,17 +108,8 @@ const NavigationProvider = forwardRef(function NavigationProvider(
119
108
}
120
109
121
110
if ( state && 'superglue' in state ) {
122
- dispatch (
123
- historyChange ( {
124
- pageKey : state . pageKey ,
125
- } )
126
- )
127
-
128
- if ( action !== 'POP' ) {
129
- return
130
- }
131
-
132
- const { pageKey, posX, posY } = state
111
+ const { pageKey } = state
112
+ const prevPageKey = store . getState ( ) . superglue . currentPageKey
133
113
const containsKey = ! ! pages [ pageKey ]
134
114
135
115
if ( containsKey ) {
@@ -138,19 +118,38 @@ const NavigationProvider = forwardRef(function NavigationProvider(
138
118
switch ( restoreStrategy ) {
139
119
case 'fromCacheOnly' :
140
120
dispatch ( setActivePage ( { pageKey } ) )
141
- setWindowScroll ( posX , posY )
142
121
break
143
122
case 'fromCacheAndRevisitInBackground' :
144
123
dispatch ( setActivePage ( { pageKey } ) )
145
- setWindowScroll ( posX , posY )
146
124
visit ( pageKey , { revisit : true } )
147
125
break
148
126
case 'revisitOnly' :
149
127
default :
150
- visitAndRestore ( pageKey , posX , posY )
128
+ visit ( pageKey , { revisit : true } ) . then ( ( ) => {
129
+ const noNav =
130
+ prevPageKey === store . getState ( ) . superglue . currentPageKey
131
+ if ( noNav ) {
132
+ // When "POP'ed", revisiting (using revisit: true) a page can result in
133
+ // a redirect, or a render of the same page.
134
+ //
135
+ // When its a redirect, calculateNavAction will correctly set the
136
+ // navigationAction to `replace` this is the noop scenario.
137
+ //
138
+ // When its the same page, navigationAction is set to `none` and
139
+ // no navigation took place. In that case, we have to set the
140
+ // activePage otherwise the user is stuck on the original page.
141
+ dispatch ( setActivePage ( { pageKey } ) )
142
+ }
143
+ } )
151
144
}
152
145
} else {
153
- visitAndRestore ( pageKey , posX , posY )
146
+ visit ( pageKey , { revisit : true } ) . then ( ( ) => {
147
+ const noNav =
148
+ prevPageKey === store . getState ( ) . superglue . currentPageKey
149
+ if ( noNav ) {
150
+ dispatch ( setActivePage ( { pageKey } ) )
151
+ }
152
+ } )
154
153
}
155
154
}
156
155
}
@@ -175,7 +174,6 @@ const NavigationProvider = forwardRef(function NavigationProvider(
175
174
if ( hasPage ) {
176
175
const location = history . location
177
176
const state = location . state as HistoryState
178
- const prevPageKey = state . pageKey
179
177
const historyArgs = [
180
178
path ,
181
179
{
@@ -196,26 +194,24 @@ const NavigationProvider = forwardRef(function NavigationProvider(
196
194
} ,
197
195
{
198
196
...state ,
199
- posY : window . pageYOffset ,
200
- posX : window . pageXOffset ,
197
+ posY : window . scrollY ,
198
+ posX : window . scrollX ,
201
199
}
202
200
)
203
201
}
204
202
205
203
history . push ( ...historyArgs )
204
+ dispatch ( setActivePage ( { pageKey : nextPageKey } ) )
206
205
}
207
206
208
207
if ( action === 'replace' ) {
209
208
history . replace ( ...historyArgs )
210
- }
211
209
212
- setActivePage ( { pageKey : nextPageKey } )
213
- setWindowScroll ( 0 , 0 )
214
-
215
- if ( action === 'replace' && prevPageKey && prevPageKey !== nextPageKey ) {
216
- dispatch ( removePage ( { pageKey : prevPageKey } ) )
210
+ if ( currentPageKey !== nextPageKey ) {
211
+ dispatch ( setActivePage ( { pageKey : nextPageKey } ) )
212
+ dispatch ( removePage ( { pageKey : currentPageKey } ) )
213
+ }
217
214
}
218
-
219
215
return true
220
216
} else {
221
217
console . warn (
@@ -228,7 +224,7 @@ const NavigationProvider = forwardRef(function NavigationProvider(
228
224
}
229
225
}
230
226
231
- const { currentPageKey , search } = superglue
227
+ const { search } = superglue
232
228
const { componentIdentifier } = pages [ currentPageKey ]
233
229
const Component = mapping [ componentIdentifier ]
234
230
0 commit comments