11export type Callback = ( newSearchParams : string ) => void ;
22
3+ declare global {
4+ interface Window {
5+ next ?: {
6+ router ?: {
7+ push : ( href : string ) => void ;
8+ } ;
9+ } ;
10+ }
11+ }
12+
313export interface UrlStateRouter {
414 push ( href : string ) : void ;
515
@@ -11,52 +21,58 @@ export type GenericRouterOptions = {
1121 poolingIntervalMs ?: number ;
1222} ;
1323
14- const subscribers = new Map < Callback , Callback > ( ) ;
15-
1624let genericRouterCurrentStateString = '' ;
1725export class GenericRouter implements UrlStateRouter {
1826 private interval : number = 0 ;
27+ private subscribers = new Map < Callback , Callback > ( ) ;
1928
2029 constructor ( private options : GenericRouterOptions ) {
2130 this . options = { poolingIntervalMs : 100 , ...options } ;
2231 }
2332
2433 push ( href : string ) : void {
25- window . history . pushState ( { } , '' , href ) ;
34+ // Use Next.js router if available
35+ // Next.js exposes a global object `window.next.router` with a `push` method for both /pages and /app routes
36+ if ( typeof window . next ?. router ?. push === 'function' ) {
37+ window . next . router . push ( href ) ;
38+ } else {
39+ window . history . pushState ( { } , '' , href ) ;
40+ }
2641 this . onSearchParamsChange ( ) ;
2742 }
2843
2944 subscribe ( fn : Callback ) : void {
30- subscribers . set ( fn , fn ) ;
45+ this . subscribers . set ( fn , fn ) ;
3146
3247 if ( ! this . interval ) {
3348 this . startPolling ( ) ;
3449 }
3550 }
3651
3752 unsubscribe ( fn : Callback ) : void {
38- subscribers . delete ( fn ) ;
53+ this . subscribers . delete ( fn ) ;
3954
40- if ( subscribers . size === 0 ) {
55+ if ( this . subscribers . size === 0 ) {
4156 this . stopPolling ( ) ;
4257 }
4358 }
4459
4560 onSearchParamsChange ( ) : void {
4661 if ( window . location . search !== genericRouterCurrentStateString ) {
4762 genericRouterCurrentStateString = window . location . search ;
48- subscribers . forEach ( ( subscriber ) =>
63+ this . subscribers . forEach ( ( subscriber ) =>
4964 subscriber ( genericRouterCurrentStateString ) ,
5065 ) ;
5166 }
5267 }
5368
5469 private startPolling ( ) : void {
5570 // 'popstate' event in browser is not reliable, so we need to poll
71+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event#when_popstate_is_sent
5672 if ( typeof window !== 'undefined' ) {
5773 this . interval = setInterval ( ( ) => {
5874 this . onSearchParamsChange ( ) ;
59- } , this . options . poolingIntervalMs ) as unknown as number ; // fix for NodeJS
75+ } , this . options . poolingIntervalMs ) as unknown as number ; // type fix for NodeJS
6076 }
6177 }
6278
0 commit comments