11import React , {
22 createContext ,
3+ startTransition ,
34 useCallback ,
5+ useContext ,
46 useEffect ,
57 useRef ,
68 useState ,
@@ -37,6 +39,29 @@ async function fetchServerSideProps(pathname: string) {
3739 throw new Error ( "Failed to fetch" ) ;
3840}
3941
42+ const VersionContext = createContext ( 0 ) ;
43+
44+ /**
45+ * a hook that returns a version number that is incremented on each route change or reload
46+ * @returns the current version (incremented on each route change or reload)
47+ */
48+ export const useLoadingVersion = ( ) => useContext ( VersionContext ) ;
49+
50+ /**
51+ * a hook that runs an effect when the version changes, which is incremented on each route change or reload
52+ * @param effect the effect to run
53+ * @param deps the dependencies
54+ */
55+ export const useLoadingEffect = (
56+ effect : React . EffectCallback ,
57+ deps : React . DependencyList = [ ]
58+ ) => {
59+ useEffect ( effect , [ useContext ( VersionContext ) , ...deps ] ) ;
60+ } ;
61+
62+ /**
63+ * a context that can be used to reload the current page
64+ */
4065export const ReloadContext = createContext ( async ( ) : Promise < void > => { } ) ;
4166
4267export const RouterHost = ( {
@@ -66,27 +91,31 @@ export const RouterHost = ({
6691 if ( props ?. redirect ) {
6792 navigate ( props . redirect ) ;
6893 } else {
69- onRouteUpdated ?.( target ) ;
70- setCurrent (
71- < Shell { ...props } >
72- < module . default { ...props ?. props } />
73- </ Shell >
74- ) ;
94+ startTransition ( ( ) => {
95+ onRouteUpdated ?.( target ) ;
96+ setCurrent (
97+ < VersionContext . Provider value = { currentVersion } >
98+ < Shell { ...props } >
99+ < module . default { ...props ?. props } />
100+ </ Shell >
101+ </ VersionContext . Provider >
102+ ) ;
103+ } ) ;
75104 }
76105 }
77106 } ,
78107 [ ]
79108 ) ;
80109 useEffect ( ( ) => {
81- if ( pathname !== globalX . __INITIAL_ROUTE__ ) {
110+ if ( pathname === globalX . __INITIAL_ROUTE__ ) {
111+ onRouteUpdated ?.( pathname ) ;
112+ // @ts -ignore
113+ delete globalX . __INITIAL_ROUTE__ ;
114+ } else {
82115 reload ( pathname ) . catch ( ( e ) => {
83116 console . log ( e ) ;
84117 location . href = pathname ;
85118 } ) ;
86- } else {
87- onRouteUpdated ?.( pathname ) ;
88- // @ts -ignore
89- delete globalX . __INITIAL_ROUTE__ ;
90119 }
91120 } , [ pathname ] ) ;
92121 return (
@@ -112,13 +141,22 @@ export function useLocationProperty<S extends Location[keyof Location]>(
112141 return useSyncExternalStore ( subscribeToLocationUpdates , fn , ssrFn ) ;
113142}
114143
144+ /**
145+ * a hook that returns the current pathname
146+ * @returns the current pathname
147+ */
115148export function usePathname ( ) {
116149 return useLocationProperty (
117150 ( ) => location . pathname ,
118151 ( ) => globalX . __INITIAL_ROUTE__
119152 ) ;
120153}
121154
155+ /**
156+ * a function that navigates/replaces to a path
157+ * @param to the path to navigate to
158+ * @param param1 the options, which can include `replace`
159+ */
122160export const navigate = ( to : string , { replace = false } = { } ) =>
123161 history [ replace ? eventReplaceState : eventPushState ] ( null , "" , to ) ;
124162
0 commit comments