1
1
import React , {
2
2
createContext ,
3
+ startTransition ,
3
4
useCallback ,
5
+ useContext ,
4
6
useEffect ,
5
7
useRef ,
6
8
useState ,
@@ -37,6 +39,29 @@ async function fetchServerSideProps(pathname: string) {
37
39
throw new Error ( "Failed to fetch" ) ;
38
40
}
39
41
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
+ */
40
65
export const ReloadContext = createContext ( async ( ) : Promise < void > => { } ) ;
41
66
42
67
export const RouterHost = ( {
@@ -66,27 +91,31 @@ export const RouterHost = ({
66
91
if ( props ?. redirect ) {
67
92
navigate ( props . redirect ) ;
68
93
} 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
+ } ) ;
75
104
}
76
105
}
77
106
} ,
78
107
[ ]
79
108
) ;
80
109
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 {
82
115
reload ( pathname ) . catch ( ( e ) => {
83
116
console . log ( e ) ;
84
117
location . href = pathname ;
85
118
} ) ;
86
- } else {
87
- onRouteUpdated ?.( pathname ) ;
88
- // @ts -ignore
89
- delete globalX . __INITIAL_ROUTE__ ;
90
119
}
91
120
} , [ pathname ] ) ;
92
121
return (
@@ -112,13 +141,22 @@ export function useLocationProperty<S extends Location[keyof Location]>(
112
141
return useSyncExternalStore ( subscribeToLocationUpdates , fn , ssrFn ) ;
113
142
}
114
143
144
+ /**
145
+ * a hook that returns the current pathname
146
+ * @returns the current pathname
147
+ */
115
148
export function usePathname ( ) {
116
149
return useLocationProperty (
117
150
( ) => location . pathname ,
118
151
( ) => globalX . __INITIAL_ROUTE__
119
152
) ;
120
153
}
121
154
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
+ */
122
160
export const navigate = ( to : string , { replace = false } = { } ) =>
123
161
history [ replace ? eventReplaceState : eventPushState ] ( null , "" , to ) ;
124
162
0 commit comments