@@ -4,6 +4,7 @@ import React, {
44 useCallback ,
55 useContext ,
66 useEffect ,
7+ useReducer ,
78 useRef ,
89 useState ,
910 useSyncExternalStore ,
@@ -83,6 +84,25 @@ export const useReloadEffect = (
8384 */
8485export const ReloadContext = createContext ( async ( ) : Promise < void > => { } ) ;
8586
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+
86106export const RouterHost = ( {
87107 children,
88108 Shell,
@@ -147,14 +167,11 @@ export const RouterHost = ({
147167} ;
148168
149169const subscribeToLocationUpdates = ( callback : ( ) => void ) => {
170+ const abort = new AbortController ( ) ;
150171 for ( const event of events ) {
151- addEventListener ( event , callback ) ;
172+ window . addEventListener ( event , callback , { signal : abort . signal } ) ;
152173 }
153- return ( ) => {
154- for ( const event of events ) {
155- removeEventListener ( event , callback ) ;
156- }
157- } ;
174+ return ( ) => abort . abort ( ) ;
158175} ;
159176
160177export function useLocationProperty < S extends Location [ keyof Location ] > (
@@ -186,21 +203,21 @@ export const navigate = (to: string, { replace = false } = {}) =>
186203const eventPopstate = "popstate" ;
187204const eventPushState = "pushState" ;
188205const eventReplaceState = "replaceState" ;
189- const eventHashchange = "hashchange" ;
190- const events = [
191- eventPopstate ,
192- eventPushState ,
193- eventReplaceState ,
194- eventHashchange ,
195- ] ;
206+ const events = [ eventPopstate , eventPushState , eventReplaceState ] ;
196207
197208if ( typeof history !== "undefined" ) {
209+ window . addEventListener ( "popstate" , ( e ) => {
210+ routeState = e . state ?? { } ;
211+ } ) ;
198212 for ( const type of [ eventPushState , eventReplaceState ] as const ) {
199213 const original = history [ type ] ;
200214 history [ type ] = function (
201- ...args : Parameters < ( typeof history ) [ typeof type ] >
215+ _data : any ,
216+ _unused : string ,
217+ url ?: string | URL | null | undefined
202218 ) {
203- const result = original . apply ( this , args ) ;
219+ const result = original . apply ( this , [ routeState , "" , url ] ) ;
220+ routeState = { } ;
204221 const event = new Event ( type ) ;
205222 unstable_batchedUpdates ( ( ) => {
206223 dispatchEvent ( event ) ;
0 commit comments