@@ -4,6 +4,7 @@ import React, {
4
4
useCallback ,
5
5
useContext ,
6
6
useEffect ,
7
+ useReducer ,
7
8
useRef ,
8
9
useState ,
9
10
useSyncExternalStore ,
@@ -83,6 +84,25 @@ export const useReloadEffect = (
83
84
*/
84
85
export const ReloadContext = createContext ( async ( ) : Promise < void > => { } ) ;
85
86
87
+ let routeState : Record < string , any > = { } ;
88
+
89
+ /**
90
+ * Returns a stateful value which bounded to route, and a function to update it.
91
+ * Note that the value won't be updated across components.
92
+ * So you should use this only in top-most component
93
+ * @experimental
94
+ * @param key unique key
95
+ * @param initial initial value
96
+ * @returns value and setter
97
+ */
98
+ export function useRouteState < T extends { } > ( key : string , initial : T ) {
99
+ return useReducer ( ( _old : T , newvalue : T ) => {
100
+ // @ts -ignore
101
+ routeState [ key ] = newvalue ;
102
+ return newvalue ;
103
+ } , ( routeState [ key ] ?? initial ) as unknown as T ) ;
104
+ }
105
+
86
106
export const RouterHost = ( {
87
107
children,
88
108
Shell,
@@ -147,14 +167,11 @@ export const RouterHost = ({
147
167
} ;
148
168
149
169
const subscribeToLocationUpdates = ( callback : ( ) => void ) => {
170
+ const abort = new AbortController ( ) ;
150
171
for ( const event of events ) {
151
- addEventListener ( event , callback ) ;
172
+ window . addEventListener ( event , callback , { signal : abort . signal } ) ;
152
173
}
153
- return ( ) => {
154
- for ( const event of events ) {
155
- removeEventListener ( event , callback ) ;
156
- }
157
- } ;
174
+ return ( ) => abort . abort ( ) ;
158
175
} ;
159
176
160
177
export function useLocationProperty < S extends Location [ keyof Location ] > (
@@ -186,21 +203,21 @@ export const navigate = (to: string, { replace = false } = {}) =>
186
203
const eventPopstate = "popstate" ;
187
204
const eventPushState = "pushState" ;
188
205
const eventReplaceState = "replaceState" ;
189
- const eventHashchange = "hashchange" ;
190
- const events = [
191
- eventPopstate ,
192
- eventPushState ,
193
- eventReplaceState ,
194
- eventHashchange ,
195
- ] ;
206
+ const events = [ eventPopstate , eventPushState , eventReplaceState ] ;
196
207
197
208
if ( typeof history !== "undefined" ) {
209
+ window . addEventListener ( "popstate" , ( e ) => {
210
+ routeState = e . state ?? { } ;
211
+ } ) ;
198
212
for ( const type of [ eventPushState , eventReplaceState ] as const ) {
199
213
const original = history [ type ] ;
200
214
history [ type ] = function (
201
- ...args : Parameters < ( typeof history ) [ typeof type ] >
215
+ _data : any ,
216
+ _unused : string ,
217
+ url ?: string | URL | null | undefined
202
218
) {
203
- const result = original . apply ( this , args ) ;
219
+ const result = original . apply ( this , [ routeState , "" , url ] ) ;
220
+ routeState = { } ;
204
221
const event = new Event ( type ) ;
205
222
unstable_batchedUpdates ( ( ) => {
206
223
dispatchEvent ( event ) ;
0 commit comments