@tanstack/react-router 0.0.1-beta.6 → 0.0.1-beta.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -18
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
- package/build/cjs/index.js +442 -0
- package/build/cjs/index.js.map +1 -0
- package/build/esm/index.js +323 -2823
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +59 -49
- package/build/stats-react.json +112 -35
- package/build/types/index.d.ts +80 -48
- package/build/umd/index.development.js +1765 -2674
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +23 -3
- package/build/umd/index.production.js.map +1 -1
- package/package.json +6 -6
- package/src/index.tsx +570 -474
- package/build/cjs/react-router/src/index.js +0 -459
- package/build/cjs/react-router/src/index.js.map +0 -1
- package/build/cjs/router-core/build/esm/index.js +0 -2524
- package/build/cjs/router-core/build/esm/index.js.map +0 -1
package/src/index.tsx
CHANGED
|
@@ -1,141 +1,69 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
|
|
3
|
-
import { useSyncExternalStore } from 'use-sync-external-store/shim'
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
AnyRoute,
|
|
7
|
-
CheckId,
|
|
8
|
-
rootRouteId,
|
|
9
|
-
Router,
|
|
10
|
-
RouterState,
|
|
11
|
-
ToIdOption,
|
|
12
|
-
} from '@tanstack/router-core'
|
|
13
3
|
import {
|
|
4
|
+
Route,
|
|
5
|
+
RegisteredRoutesInfo,
|
|
6
|
+
RegisteredRouter,
|
|
7
|
+
RouterStore,
|
|
8
|
+
last,
|
|
14
9
|
warning,
|
|
15
10
|
RouterOptions,
|
|
16
11
|
RouteMatch,
|
|
17
12
|
MatchRouteOptions,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
DefaultAllRouteInfo,
|
|
13
|
+
AnyRoute,
|
|
14
|
+
AnyRoutesInfo,
|
|
15
|
+
DefaultRoutesInfo,
|
|
22
16
|
functionalUpdate,
|
|
23
|
-
|
|
24
|
-
AnyRouteInfo,
|
|
25
|
-
AllRouteInfo,
|
|
26
|
-
RouteInfo,
|
|
17
|
+
RoutesInfo,
|
|
27
18
|
ValidFromPath,
|
|
28
19
|
LinkOptions,
|
|
29
|
-
|
|
20
|
+
RouteByPath,
|
|
30
21
|
ResolveRelativePath,
|
|
31
22
|
NoInfer,
|
|
32
23
|
ToOptions,
|
|
33
24
|
invariant,
|
|
34
|
-
|
|
25
|
+
Router,
|
|
26
|
+
Expand,
|
|
27
|
+
} from '@tanstack/router'
|
|
28
|
+
import { useStore } from '@tanstack/react-store'
|
|
35
29
|
|
|
36
|
-
|
|
30
|
+
//
|
|
37
31
|
|
|
38
|
-
|
|
39
|
-
interface FrameworkGenerics {
|
|
40
|
-
Element: React.ReactNode
|
|
41
|
-
// Any is required here so import() will work without having to do import().then(d => d.default)
|
|
42
|
-
SyncOrAsyncElement: React.ReactNode | (() => Promise<any>)
|
|
43
|
-
}
|
|
32
|
+
export * from '@tanstack/router'
|
|
44
33
|
|
|
45
|
-
|
|
46
|
-
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
47
|
-
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
48
|
-
> {
|
|
49
|
-
useState: () => RouterState
|
|
50
|
-
useRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
51
|
-
routeId: TId,
|
|
52
|
-
) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
53
|
-
useMatch: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
54
|
-
routeId: TId,
|
|
55
|
-
) => RouteMatch<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
56
|
-
linkProps: <TTo extends string = '.'>(
|
|
57
|
-
props: LinkPropsOptions<TAllRouteInfo, '/', TTo> &
|
|
58
|
-
React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
59
|
-
) => React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
60
|
-
Link: <TTo extends string = '.'>(
|
|
61
|
-
props: LinkPropsOptions<TAllRouteInfo, '/', TTo> &
|
|
62
|
-
React.AnchorHTMLAttributes<HTMLAnchorElement> &
|
|
63
|
-
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
|
|
64
|
-
// If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
|
|
65
|
-
children?:
|
|
66
|
-
| React.ReactNode
|
|
67
|
-
| ((state: { isActive: boolean }) => React.ReactNode)
|
|
68
|
-
},
|
|
69
|
-
) => JSX.Element
|
|
70
|
-
MatchRoute: <TTo extends string = '.'>(
|
|
71
|
-
props: ToOptions<TAllRouteInfo, '/', TTo> &
|
|
72
|
-
MatchRouteOptions & {
|
|
73
|
-
// If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
|
|
74
|
-
children?:
|
|
75
|
-
| React.ReactNode
|
|
76
|
-
| ((
|
|
77
|
-
params: RouteInfoByPath<
|
|
78
|
-
TAllRouteInfo,
|
|
79
|
-
ResolveRelativePath<'/', NoInfer<TTo>>
|
|
80
|
-
>['allParams'],
|
|
81
|
-
) => React.ReactNode)
|
|
82
|
-
},
|
|
83
|
-
) => JSX.Element
|
|
84
|
-
}
|
|
34
|
+
export { useStore }
|
|
85
35
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
props: LinkPropsOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo> &
|
|
109
|
-
React.AnchorHTMLAttributes<HTMLAnchorElement> &
|
|
110
|
-
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
|
|
111
|
-
// If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
|
|
112
|
-
children?:
|
|
113
|
-
| React.ReactNode
|
|
114
|
-
| ((state: { isActive: boolean }) => React.ReactNode)
|
|
115
|
-
},
|
|
116
|
-
) => JSX.Element
|
|
117
|
-
MatchRoute: <TTo extends string = '.'>(
|
|
118
|
-
props: ToOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo> &
|
|
119
|
-
MatchRouteOptions & {
|
|
120
|
-
// If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
|
|
121
|
-
children?:
|
|
122
|
-
| React.ReactNode
|
|
123
|
-
| ((
|
|
124
|
-
params: RouteInfoByPath<
|
|
125
|
-
TAllRouteInfo,
|
|
126
|
-
ResolveRelativePath<TRouteInfo['fullPath'], NoInfer<TTo>>
|
|
127
|
-
>['allParams'],
|
|
128
|
-
) => React.ReactNode)
|
|
129
|
-
},
|
|
130
|
-
) => JSX.Element
|
|
36
|
+
//
|
|
37
|
+
|
|
38
|
+
type ReactNode = any
|
|
39
|
+
|
|
40
|
+
export type SyncRouteComponent<TProps = {}> = (props: TProps) => ReactNode
|
|
41
|
+
|
|
42
|
+
export type RouteComponent<TProps = {}> = SyncRouteComponent<TProps> & {
|
|
43
|
+
preload?: () => Promise<void>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function lazy(
|
|
47
|
+
importer: () => Promise<{ default: SyncRouteComponent }>,
|
|
48
|
+
): RouteComponent {
|
|
49
|
+
const lazyComp = React.lazy(importer as any)
|
|
50
|
+
let preloaded: Promise<SyncRouteComponent>
|
|
51
|
+
|
|
52
|
+
const finalComp = lazyComp as unknown as RouteComponent
|
|
53
|
+
|
|
54
|
+
finalComp.preload = async () => {
|
|
55
|
+
if (!preloaded) {
|
|
56
|
+
await importer()
|
|
57
|
+
}
|
|
131
58
|
}
|
|
59
|
+
|
|
60
|
+
return finalComp
|
|
132
61
|
}
|
|
133
62
|
|
|
134
|
-
type LinkPropsOptions<
|
|
135
|
-
|
|
136
|
-
TFrom extends ValidFromPath<TAllRouteInfo> = '/',
|
|
63
|
+
export type LinkPropsOptions<
|
|
64
|
+
TFrom extends RegisteredRoutesInfo['routePaths'] = '/',
|
|
137
65
|
TTo extends string = '.',
|
|
138
|
-
> = LinkOptions<
|
|
66
|
+
> = LinkOptions<RegisteredRoutesInfo, TFrom, TTo> & {
|
|
139
67
|
// A function that returns additional props for the `active` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
|
|
140
68
|
activeProps?:
|
|
141
69
|
| React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
@@ -146,414 +74,592 @@ type LinkPropsOptions<
|
|
|
146
74
|
| (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
|
|
147
75
|
}
|
|
148
76
|
|
|
77
|
+
export type MakeUseMatchRouteOptions<
|
|
78
|
+
TFrom extends RegisteredRoutesInfo['routePaths'] = '/',
|
|
79
|
+
TTo extends string = '.',
|
|
80
|
+
> = ToOptions<RegisteredRoutesInfo, TFrom, TTo> & MatchRouteOptions
|
|
81
|
+
|
|
82
|
+
export type MakeMatchRouteOptions<
|
|
83
|
+
TFrom extends RegisteredRoutesInfo['routePaths'] = '/',
|
|
84
|
+
TTo extends string = '.',
|
|
85
|
+
> = ToOptions<RegisteredRoutesInfo, TFrom, TTo> &
|
|
86
|
+
MatchRouteOptions & {
|
|
87
|
+
// If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
|
|
88
|
+
children?:
|
|
89
|
+
| ReactNode
|
|
90
|
+
| ((
|
|
91
|
+
params: RouteByPath<
|
|
92
|
+
RegisteredRoutesInfo,
|
|
93
|
+
ResolveRelativePath<TFrom, NoInfer<TTo>>
|
|
94
|
+
>['__types']['allParams'],
|
|
95
|
+
) => ReactNode)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export type MakeLinkPropsOptions<
|
|
99
|
+
TFrom extends ValidFromPath<RegisteredRoutesInfo> = '/',
|
|
100
|
+
TTo extends string = '.',
|
|
101
|
+
> = LinkPropsOptions<TFrom, TTo> & React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
102
|
+
|
|
103
|
+
export type MakeLinkOptions<
|
|
104
|
+
TFrom extends RegisteredRoutesInfo['routePaths'] = '/',
|
|
105
|
+
TTo extends string = '.',
|
|
106
|
+
> = LinkPropsOptions<TFrom, TTo> &
|
|
107
|
+
React.AnchorHTMLAttributes<HTMLAnchorElement> &
|
|
108
|
+
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
|
|
109
|
+
// If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
|
|
110
|
+
children?: ReactNode | ((state: { isActive: boolean }) => ReactNode)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
declare module '@tanstack/router' {
|
|
114
|
+
interface FrameworkGenerics {
|
|
115
|
+
Component: RouteComponent
|
|
116
|
+
ErrorComponent: RouteComponent<{
|
|
117
|
+
error: unknown
|
|
118
|
+
info: { componentStack: string }
|
|
119
|
+
}>
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
interface RouterOptions<TRouteTree, TRouterContext> {
|
|
123
|
+
// ssrFooter?: () => JSX.Element | Node
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
149
127
|
export type PromptProps = {
|
|
150
128
|
message: string
|
|
151
129
|
when?: boolean | any
|
|
152
|
-
children?:
|
|
130
|
+
children?: ReactNode
|
|
153
131
|
}
|
|
154
132
|
|
|
155
133
|
//
|
|
156
134
|
|
|
157
|
-
|
|
158
|
-
|
|
135
|
+
export function useLinkProps<
|
|
136
|
+
TFrom extends ValidFromPath<RegisteredRoutesInfo> = '/',
|
|
137
|
+
TTo extends string = '.',
|
|
138
|
+
>(
|
|
139
|
+
options: MakeLinkPropsOptions<TFrom, TTo>,
|
|
140
|
+
): React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
141
|
+
const router = useRouter()
|
|
159
142
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
)
|
|
143
|
+
const {
|
|
144
|
+
// custom props
|
|
145
|
+
type,
|
|
146
|
+
children,
|
|
147
|
+
target,
|
|
148
|
+
activeProps = () => ({ className: 'active' }),
|
|
149
|
+
inactiveProps = () => ({}),
|
|
150
|
+
activeOptions,
|
|
151
|
+
disabled,
|
|
152
|
+
// fromCurrent,
|
|
153
|
+
hash,
|
|
154
|
+
search,
|
|
155
|
+
params,
|
|
156
|
+
to = '.',
|
|
157
|
+
preload,
|
|
158
|
+
preloadDelay,
|
|
159
|
+
preloadMaxAge,
|
|
160
|
+
replace,
|
|
161
|
+
// element props
|
|
162
|
+
style,
|
|
163
|
+
className,
|
|
164
|
+
onClick,
|
|
165
|
+
onFocus,
|
|
166
|
+
onMouseEnter,
|
|
167
|
+
onMouseLeave,
|
|
168
|
+
onTouchStart,
|
|
169
|
+
onTouchEnd,
|
|
170
|
+
...rest
|
|
171
|
+
} = options
|
|
172
|
+
|
|
173
|
+
const linkInfo = router.buildLink(options as any)
|
|
174
|
+
|
|
175
|
+
if (linkInfo.type === 'external') {
|
|
176
|
+
const { href } = linkInfo
|
|
177
|
+
return { href }
|
|
178
|
+
}
|
|
166
179
|
|
|
167
|
-
const
|
|
180
|
+
const { handleClick, handleFocus, handleEnter, handleLeave, isActive, next } =
|
|
181
|
+
linkInfo
|
|
182
|
+
|
|
183
|
+
const reactHandleClick = (e: Event) => {
|
|
184
|
+
if (React.startTransition) {
|
|
185
|
+
// This is a hack for react < 18
|
|
186
|
+
React.startTransition(() => {
|
|
187
|
+
handleClick(e)
|
|
188
|
+
})
|
|
189
|
+
} else {
|
|
190
|
+
handleClick(e)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
168
193
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
194
|
+
const composeHandlers =
|
|
195
|
+
(handlers: (undefined | ((e: any) => void))[]) =>
|
|
196
|
+
(e: React.SyntheticEvent) => {
|
|
197
|
+
if (e.persist) e.persist()
|
|
198
|
+
handlers.filter(Boolean).forEach((handler) => {
|
|
199
|
+
if (e.defaultPrevented) return
|
|
200
|
+
handler!(e)
|
|
201
|
+
})
|
|
202
|
+
}
|
|
173
203
|
|
|
174
|
-
|
|
175
|
-
|
|
204
|
+
// Get the active props
|
|
205
|
+
const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> = isActive
|
|
206
|
+
? functionalUpdate(activeProps as any, {}) ?? {}
|
|
207
|
+
: {}
|
|
208
|
+
|
|
209
|
+
// Get the inactive props
|
|
210
|
+
const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =
|
|
211
|
+
isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
...resolvedActiveProps,
|
|
215
|
+
...resolvedInactiveProps,
|
|
216
|
+
...rest,
|
|
217
|
+
href: disabled ? undefined : next.href,
|
|
218
|
+
onClick: composeHandlers([onClick, reactHandleClick]),
|
|
219
|
+
onFocus: composeHandlers([onFocus, handleFocus]),
|
|
220
|
+
onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
|
|
221
|
+
onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
|
|
222
|
+
target,
|
|
223
|
+
style: {
|
|
224
|
+
...style,
|
|
225
|
+
...resolvedActiveProps.style,
|
|
226
|
+
...resolvedInactiveProps.style,
|
|
227
|
+
},
|
|
228
|
+
className:
|
|
229
|
+
[
|
|
230
|
+
className,
|
|
231
|
+
resolvedActiveProps.className,
|
|
232
|
+
resolvedInactiveProps.className,
|
|
233
|
+
]
|
|
234
|
+
.filter(Boolean)
|
|
235
|
+
.join(' ') || undefined,
|
|
236
|
+
...(disabled
|
|
237
|
+
? {
|
|
238
|
+
role: 'link',
|
|
239
|
+
'aria-disabled': true,
|
|
240
|
+
}
|
|
241
|
+
: undefined),
|
|
242
|
+
['data-status']: isActive ? 'active' : undefined,
|
|
243
|
+
}
|
|
176
244
|
}
|
|
177
245
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
246
|
+
export interface LinkFn<
|
|
247
|
+
TDefaultFrom extends RegisteredRoutesInfo['routePaths'] = '/',
|
|
248
|
+
TDefaultTo extends string = '.',
|
|
249
|
+
> {
|
|
250
|
+
<
|
|
251
|
+
TFrom extends RegisteredRoutesInfo['routePaths'] = TDefaultFrom,
|
|
252
|
+
TTo extends string = TDefaultTo,
|
|
253
|
+
>(
|
|
254
|
+
props: MakeLinkOptions<TFrom, TTo> & React.RefAttributes<HTMLAnchorElement>,
|
|
255
|
+
): ReactNode
|
|
184
256
|
}
|
|
185
257
|
|
|
186
|
-
export
|
|
187
|
-
|
|
188
|
-
>(opts: RouterOptions<TRouteConfig>): Router<TRouteConfig> {
|
|
189
|
-
const makeRouteExt = (
|
|
190
|
-
route: AnyRoute,
|
|
191
|
-
router: Router<any, any>,
|
|
192
|
-
): Pick<AnyRoute, 'useRoute' | 'linkProps' | 'Link' | 'MatchRoute'> => {
|
|
193
|
-
return {
|
|
194
|
-
useRoute: (subRouteId = '.' as any) => {
|
|
195
|
-
const resolvedRouteId = router.resolvePath(
|
|
196
|
-
route.routeId,
|
|
197
|
-
subRouteId as string,
|
|
198
|
-
)
|
|
199
|
-
const resolvedRoute = router.getRoute(resolvedRouteId)
|
|
200
|
-
useRouterSubscription(router)
|
|
201
|
-
invariant(
|
|
202
|
-
resolvedRoute,
|
|
203
|
-
`Could not find a route for route "${
|
|
204
|
-
resolvedRouteId as string
|
|
205
|
-
}"! Did you forget to add it to your route config?`,
|
|
206
|
-
)
|
|
207
|
-
return resolvedRoute
|
|
208
|
-
},
|
|
209
|
-
linkProps: (options) => {
|
|
210
|
-
const {
|
|
211
|
-
// custom props
|
|
212
|
-
type,
|
|
213
|
-
children,
|
|
214
|
-
target,
|
|
215
|
-
activeProps = () => ({ className: 'active' }),
|
|
216
|
-
inactiveProps = () => ({}),
|
|
217
|
-
activeOptions,
|
|
218
|
-
disabled,
|
|
219
|
-
// fromCurrent,
|
|
220
|
-
hash,
|
|
221
|
-
search,
|
|
222
|
-
params,
|
|
223
|
-
to,
|
|
224
|
-
preload,
|
|
225
|
-
preloadDelay,
|
|
226
|
-
preloadMaxAge,
|
|
227
|
-
replace,
|
|
228
|
-
// element props
|
|
229
|
-
style,
|
|
230
|
-
className,
|
|
231
|
-
onClick,
|
|
232
|
-
onFocus,
|
|
233
|
-
onMouseEnter,
|
|
234
|
-
onMouseLeave,
|
|
235
|
-
onTouchStart,
|
|
236
|
-
onTouchEnd,
|
|
237
|
-
...rest
|
|
238
|
-
} = options
|
|
239
|
-
|
|
240
|
-
const linkInfo = route.buildLink(options)
|
|
241
|
-
|
|
242
|
-
if (linkInfo.type === 'external') {
|
|
243
|
-
const { href } = linkInfo
|
|
244
|
-
return { href }
|
|
245
|
-
}
|
|
258
|
+
export const Link: LinkFn = React.forwardRef((props: any, ref) => {
|
|
259
|
+
const linkProps = useLinkProps(props)
|
|
246
260
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
})
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Get the active props
|
|
266
|
-
const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> =
|
|
267
|
-
isActive ? functionalUpdate(activeProps, {}) ?? {} : {}
|
|
268
|
-
|
|
269
|
-
// Get the inactive props
|
|
270
|
-
const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =
|
|
271
|
-
isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {}
|
|
272
|
-
|
|
273
|
-
return {
|
|
274
|
-
...resolvedActiveProps,
|
|
275
|
-
...resolvedInactiveProps,
|
|
276
|
-
...rest,
|
|
277
|
-
href: disabled ? undefined : next.href,
|
|
278
|
-
onClick: composeHandlers([handleClick, onClick]),
|
|
279
|
-
onFocus: composeHandlers([handleFocus, onFocus]),
|
|
280
|
-
onMouseEnter: composeHandlers([handleEnter, onMouseEnter]),
|
|
281
|
-
onMouseLeave: composeHandlers([handleLeave, onMouseLeave]),
|
|
282
|
-
target,
|
|
283
|
-
style: {
|
|
284
|
-
...style,
|
|
285
|
-
...resolvedActiveProps.style,
|
|
286
|
-
...resolvedInactiveProps.style,
|
|
287
|
-
},
|
|
288
|
-
className:
|
|
289
|
-
[
|
|
290
|
-
className,
|
|
291
|
-
resolvedActiveProps.className,
|
|
292
|
-
resolvedInactiveProps.className,
|
|
293
|
-
]
|
|
294
|
-
.filter(Boolean)
|
|
295
|
-
.join(' ') || undefined,
|
|
296
|
-
...(disabled
|
|
297
|
-
? {
|
|
298
|
-
role: 'link',
|
|
299
|
-
'aria-disabled': true,
|
|
300
|
-
}
|
|
301
|
-
: undefined),
|
|
302
|
-
['data-status']: isActive ? 'active' : undefined,
|
|
303
|
-
}
|
|
304
|
-
},
|
|
305
|
-
Link: React.forwardRef((props: any, ref) => {
|
|
306
|
-
const linkProps = route.linkProps(props)
|
|
307
|
-
|
|
308
|
-
useRouterSubscription(router)
|
|
309
|
-
|
|
310
|
-
return (
|
|
311
|
-
<a
|
|
312
|
-
{...{
|
|
313
|
-
ref: ref as any,
|
|
314
|
-
...linkProps,
|
|
315
|
-
children:
|
|
316
|
-
typeof props.children === 'function'
|
|
317
|
-
? props.children({
|
|
318
|
-
isActive: (linkProps as any)['data-status'] === 'active',
|
|
319
|
-
})
|
|
320
|
-
: props.children,
|
|
321
|
-
}}
|
|
322
|
-
/>
|
|
323
|
-
)
|
|
324
|
-
}) as any,
|
|
325
|
-
MatchRoute: (opts) => {
|
|
326
|
-
const { pending, caseSensitive, children, ...rest } = opts
|
|
327
|
-
|
|
328
|
-
const params = route.matchRoute(rest as any, {
|
|
329
|
-
pending,
|
|
330
|
-
caseSensitive,
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
if (!params) {
|
|
334
|
-
return null
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return typeof opts.children === 'function'
|
|
338
|
-
? opts.children(params as any)
|
|
339
|
-
: (opts.children as any)
|
|
340
|
-
},
|
|
341
|
-
}
|
|
342
|
-
}
|
|
261
|
+
return (
|
|
262
|
+
<a
|
|
263
|
+
{...{
|
|
264
|
+
ref: ref as any,
|
|
265
|
+
...linkProps,
|
|
266
|
+
children:
|
|
267
|
+
typeof props.children === 'function'
|
|
268
|
+
? props.children({
|
|
269
|
+
isActive: (linkProps as any)['data-status'] === 'active',
|
|
270
|
+
})
|
|
271
|
+
: props.children,
|
|
272
|
+
}}
|
|
273
|
+
/>
|
|
274
|
+
)
|
|
275
|
+
}) as any
|
|
343
276
|
|
|
344
|
-
|
|
345
|
-
...opts,
|
|
346
|
-
createRouter: (router) => {
|
|
347
|
-
const routerExt: Pick<Router<any, any>, 'useMatch' | 'useState'> = {
|
|
348
|
-
useState: () => {
|
|
349
|
-
useRouterSubscription(router)
|
|
350
|
-
return router.state
|
|
351
|
-
},
|
|
352
|
-
useMatch: (routeId) => {
|
|
353
|
-
useRouterSubscription(router)
|
|
354
|
-
|
|
355
|
-
invariant(
|
|
356
|
-
routeId !== rootRouteId,
|
|
357
|
-
`"${rootRouteId}" cannot be used with useMatch! Did you mean to useRoute("${rootRouteId}")?`,
|
|
358
|
-
)
|
|
359
|
-
|
|
360
|
-
const runtimeMatch = useMatch()
|
|
361
|
-
const match = router.state.matches.find((d) => d.routeId === routeId)
|
|
362
|
-
|
|
363
|
-
invariant(
|
|
364
|
-
match,
|
|
365
|
-
`Could not find a match for route "${
|
|
366
|
-
routeId as string
|
|
367
|
-
}" being rendered in this component!`,
|
|
368
|
-
)
|
|
369
|
-
|
|
370
|
-
invariant(
|
|
371
|
-
runtimeMatch.routeId == match?.routeId,
|
|
372
|
-
`useMatch('${
|
|
373
|
-
match?.routeId as string
|
|
374
|
-
}') is being called in a component that is meant to render the '${
|
|
375
|
-
runtimeMatch.routeId
|
|
376
|
-
}' route. Did you mean to 'useRoute(${
|
|
377
|
-
match?.routeId as string
|
|
378
|
-
})' instead?`,
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
if (!match) {
|
|
382
|
-
invariant('Match not found!')
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
return match
|
|
386
|
-
},
|
|
387
|
-
}
|
|
277
|
+
type MatchesContextValue = RouteMatch[]
|
|
388
278
|
|
|
389
|
-
|
|
279
|
+
export const matchesContext = React.createContext<MatchesContextValue>(null!)
|
|
280
|
+
export const routerContext = React.createContext<{ router: RegisteredRouter }>(
|
|
281
|
+
null!,
|
|
282
|
+
)
|
|
390
283
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
284
|
+
export type MatchesProviderProps = {
|
|
285
|
+
value: MatchesContextValue
|
|
286
|
+
children: ReactNode
|
|
287
|
+
}
|
|
395
288
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
289
|
+
export class ReactRouter<
|
|
290
|
+
TRouteConfig extends AnyRoute = Route,
|
|
291
|
+
TRoutesInfo extends AnyRoutesInfo = RoutesInfo<TRouteConfig>,
|
|
292
|
+
TRouterContext = unknown,
|
|
293
|
+
> extends Router<TRouteConfig, TRoutesInfo, TRouterContext> {
|
|
294
|
+
constructor(opts: RouterOptions<TRouteConfig, TRouterContext>) {
|
|
295
|
+
super({
|
|
296
|
+
...opts,
|
|
297
|
+
loadComponent: async (component) => {
|
|
298
|
+
if (component.preload) {
|
|
299
|
+
await component.preload()
|
|
407
300
|
}
|
|
408
|
-
}
|
|
409
301
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
return coreRouter as any
|
|
302
|
+
return component as any
|
|
303
|
+
},
|
|
304
|
+
})
|
|
305
|
+
}
|
|
415
306
|
}
|
|
416
307
|
|
|
417
308
|
export type RouterProps<
|
|
418
|
-
TRouteConfig extends
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
children?: React.ReactNode
|
|
309
|
+
TRouteConfig extends AnyRoute = Route,
|
|
310
|
+
TRoutesInfo extends AnyRoutesInfo = DefaultRoutesInfo,
|
|
311
|
+
TRouterContext = unknown,
|
|
312
|
+
> = RouterOptions<TRouteConfig, TRouterContext> & {
|
|
313
|
+
router: Router<TRouteConfig, TRoutesInfo, TRouterContext>
|
|
424
314
|
}
|
|
425
315
|
|
|
426
316
|
export function RouterProvider<
|
|
427
|
-
TRouteConfig extends
|
|
428
|
-
|
|
429
|
-
|
|
317
|
+
TRouteConfig extends AnyRoute = Route,
|
|
318
|
+
TRoutesInfo extends AnyRoutesInfo = DefaultRoutesInfo,
|
|
319
|
+
TRouterContext = unknown,
|
|
320
|
+
>({ router, ...rest }: RouterProps<TRouteConfig, TRoutesInfo, TRouterContext>) {
|
|
430
321
|
router.update(rest)
|
|
431
322
|
|
|
432
|
-
|
|
323
|
+
const currentMatches = useStore(
|
|
324
|
+
router.store,
|
|
325
|
+
(s) => s.currentMatches,
|
|
326
|
+
undefined,
|
|
327
|
+
)
|
|
433
328
|
|
|
434
|
-
|
|
435
|
-
return router.mount()
|
|
436
|
-
}, [router])
|
|
329
|
+
React.useEffect(router.mount, [router])
|
|
437
330
|
|
|
438
331
|
return (
|
|
439
|
-
|
|
440
|
-
<
|
|
441
|
-
{
|
|
442
|
-
|
|
443
|
-
|
|
332
|
+
<>
|
|
333
|
+
<routerContext.Provider value={{ router: router as any }}>
|
|
334
|
+
<matchesContext.Provider value={[undefined!, ...currentMatches]}>
|
|
335
|
+
<Outlet />
|
|
336
|
+
</matchesContext.Provider>
|
|
337
|
+
</routerContext.Provider>
|
|
338
|
+
</>
|
|
444
339
|
)
|
|
445
340
|
}
|
|
446
341
|
|
|
447
|
-
function useRouter():
|
|
342
|
+
export function useRouter(): RegisteredRouter {
|
|
448
343
|
const value = React.useContext(routerContext)
|
|
449
344
|
warning(!value, 'useRouter must be used inside a <Router> component!')
|
|
345
|
+
return value.router
|
|
346
|
+
}
|
|
450
347
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
348
|
+
export function useRouterStore<T = RouterStore>(
|
|
349
|
+
selector?: (state: Router['store']) => T,
|
|
350
|
+
shallow?: boolean,
|
|
351
|
+
): T {
|
|
352
|
+
const router = useRouter()
|
|
353
|
+
return useStore(router.store, selector as any, shallow)
|
|
454
354
|
}
|
|
455
355
|
|
|
456
|
-
function useMatches(): RouteMatch[] {
|
|
356
|
+
export function useMatches(): RouteMatch[] {
|
|
457
357
|
return React.useContext(matchesContext)
|
|
458
358
|
}
|
|
459
359
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
360
|
+
export function useMatch<
|
|
361
|
+
TFrom extends keyof RegisteredRoutesInfo['routesById'],
|
|
362
|
+
TStrict extends boolean = true,
|
|
363
|
+
TRouteMatch = RouteMatch<
|
|
364
|
+
RegisteredRoutesInfo,
|
|
365
|
+
RegisteredRoutesInfo['routesById'][TFrom]
|
|
366
|
+
>,
|
|
367
|
+
>(opts?: {
|
|
368
|
+
from: TFrom
|
|
369
|
+
strict?: TStrict
|
|
370
|
+
track?: (match: TRouteMatch) => any
|
|
371
|
+
shallow?: boolean
|
|
372
|
+
}): TStrict extends true ? TRouteMatch : TRouteMatch | undefined {
|
|
373
|
+
const router = useRouter()
|
|
374
|
+
const nearestMatch = useMatches()[0]!
|
|
375
|
+
const match = opts?.from
|
|
376
|
+
? router.store.state.currentMatches.find((d) => d.route.id === opts?.from)
|
|
377
|
+
: nearestMatch
|
|
378
|
+
|
|
379
|
+
invariant(
|
|
380
|
+
match,
|
|
381
|
+
`Could not find ${
|
|
382
|
+
opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'
|
|
383
|
+
}`,
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
if (opts?.strict ?? true) {
|
|
387
|
+
invariant(
|
|
388
|
+
nearestMatch.route.id == match?.route.id,
|
|
389
|
+
`useMatch("${
|
|
390
|
+
match?.route.id as string
|
|
391
|
+
}") is being called in a component that is meant to render the '${
|
|
392
|
+
nearestMatch.route.id
|
|
393
|
+
}' route. Did you mean to 'useMatch("${
|
|
394
|
+
match?.route.id as string
|
|
395
|
+
}", { strict: false })' or 'useRoute("${
|
|
396
|
+
match?.route.id as string
|
|
397
|
+
}")' instead?`,
|
|
398
|
+
)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
useStore(
|
|
402
|
+
match!.store,
|
|
403
|
+
(d) => opts?.track?.(match as any) ?? match,
|
|
404
|
+
opts?.shallow,
|
|
405
|
+
)
|
|
469
406
|
|
|
470
|
-
|
|
471
|
-
return useMatches()?.[0] as RouteMatch
|
|
407
|
+
return match as any
|
|
472
408
|
}
|
|
473
409
|
|
|
474
|
-
export function
|
|
410
|
+
export function useRoute<
|
|
411
|
+
TId extends keyof RegisteredRoutesInfo['routesById'] = '/',
|
|
412
|
+
>(routeId: TId): RegisteredRoutesInfo['routesById'][TId] {
|
|
475
413
|
const router = useRouter()
|
|
476
|
-
const
|
|
414
|
+
const resolvedRoute = router.getRoute(routeId as any)
|
|
477
415
|
|
|
478
|
-
|
|
416
|
+
invariant(
|
|
417
|
+
resolvedRoute,
|
|
418
|
+
`Could not find a route for route "${
|
|
419
|
+
routeId as string
|
|
420
|
+
}"! Did you forget to add it to your route config?`,
|
|
421
|
+
)
|
|
479
422
|
|
|
480
|
-
|
|
423
|
+
return resolvedRoute as any
|
|
424
|
+
}
|
|
481
425
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
426
|
+
export function useSearch<
|
|
427
|
+
TFrom extends keyof RegisteredRoutesInfo['routesById'],
|
|
428
|
+
TStrict extends boolean = true,
|
|
429
|
+
TSearch = RegisteredRoutesInfo['routesById'][TFrom]['__types']['fullSearchSchema'],
|
|
430
|
+
TSelected = TSearch,
|
|
431
|
+
>(opts?: {
|
|
432
|
+
from: TFrom
|
|
433
|
+
strict?: TStrict
|
|
434
|
+
track?: (search: TSearch) => TSelected
|
|
435
|
+
}): TStrict extends true ? TSelected : TSelected | undefined {
|
|
436
|
+
const match = useMatch(opts)
|
|
437
|
+
useStore(
|
|
438
|
+
(match as any).store,
|
|
439
|
+
(d: any) => opts?.track?.(d.search) ?? d.search,
|
|
440
|
+
)
|
|
486
441
|
|
|
487
|
-
|
|
488
|
-
|
|
442
|
+
return (match as unknown as RouteMatch).store.state.search as any
|
|
443
|
+
}
|
|
489
444
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
445
|
+
export function useParams<
|
|
446
|
+
TFrom extends keyof RegisteredRoutesInfo['routesById'] = '/',
|
|
447
|
+
TDefaultSelected = Expand<
|
|
448
|
+
RegisteredRoutesInfo['allParams'] &
|
|
449
|
+
RegisteredRoutesInfo['routesById'][TFrom]['__types']['allParams']
|
|
450
|
+
>,
|
|
451
|
+
TSelected = TDefaultSelected,
|
|
452
|
+
>(opts?: {
|
|
453
|
+
from: TFrom
|
|
454
|
+
track?: (search: TDefaultSelected) => TSelected
|
|
455
|
+
}): TSelected {
|
|
456
|
+
const router = useRouter()
|
|
457
|
+
useStore(router.store, (d) => {
|
|
458
|
+
const params = last(d.currentMatches)?.params as any
|
|
459
|
+
return opts?.track?.(params) ?? params
|
|
460
|
+
})
|
|
494
461
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
router.options.useErrorBoundary
|
|
498
|
-
) {
|
|
499
|
-
throw childMatch.error
|
|
500
|
-
}
|
|
462
|
+
return last(router.store.state.currentMatches)?.params as any
|
|
463
|
+
}
|
|
501
464
|
|
|
502
|
-
|
|
503
|
-
|
|
465
|
+
export function useNavigate<
|
|
466
|
+
TDefaultFrom extends keyof RegisteredRoutesInfo['routesById'] = '/',
|
|
467
|
+
>(defaultOpts?: { from?: TDefaultFrom }) {
|
|
468
|
+
const router = useRouter()
|
|
469
|
+
return <
|
|
470
|
+
TFrom extends keyof RegisteredRoutesInfo['routesById'] = TDefaultFrom,
|
|
471
|
+
TTo extends string = '.',
|
|
472
|
+
>(
|
|
473
|
+
opts?: MakeLinkOptions<TFrom, TTo>,
|
|
474
|
+
) => {
|
|
475
|
+
return router.navigate({ ...defaultOpts, ...(opts as any) })
|
|
476
|
+
}
|
|
477
|
+
}
|
|
504
478
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
const pendingElement =
|
|
508
|
-
childMatch.__.pendingElement ?? router.options.defaultPendingElement
|
|
479
|
+
export function useMatchRoute() {
|
|
480
|
+
const router = useRouter()
|
|
509
481
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
482
|
+
return <
|
|
483
|
+
TFrom extends ValidFromPath<RegisteredRoutesInfo> = '/',
|
|
484
|
+
TTo extends string = '.',
|
|
485
|
+
>(
|
|
486
|
+
opts: MakeUseMatchRouteOptions<TFrom, TTo>,
|
|
487
|
+
) => {
|
|
488
|
+
const { pending, caseSensitive, ...rest } = opts
|
|
489
|
+
|
|
490
|
+
return router.matchRoute(rest as any, {
|
|
491
|
+
pending,
|
|
492
|
+
caseSensitive,
|
|
493
|
+
})
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
export function MatchRoute<
|
|
498
|
+
TFrom extends ValidFromPath<RegisteredRoutesInfo> = '/',
|
|
499
|
+
TTo extends string = '.',
|
|
500
|
+
>(props: MakeMatchRouteOptions<TFrom, TTo>): any {
|
|
501
|
+
const matchRoute = useMatchRoute()
|
|
502
|
+
const params = matchRoute(props)
|
|
503
|
+
|
|
504
|
+
if (!params) {
|
|
505
|
+
return null
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (typeof props.children === 'function') {
|
|
509
|
+
return (props.children as any)(params)
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return params ? props.children : null
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export function Outlet() {
|
|
516
|
+
const matches = useMatches().slice(1)
|
|
517
|
+
const match = matches[0]
|
|
514
518
|
|
|
515
|
-
|
|
519
|
+
if (!match) {
|
|
520
|
+
return null
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return <SubOutlet matches={matches} match={match} />
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function SubOutlet({
|
|
527
|
+
matches,
|
|
528
|
+
match,
|
|
529
|
+
}: {
|
|
530
|
+
matches: RouteMatch[]
|
|
531
|
+
match: RouteMatch
|
|
532
|
+
}) {
|
|
533
|
+
const router = useRouter()
|
|
534
|
+
useStore(match!.store)
|
|
535
|
+
|
|
536
|
+
const defaultPending = React.useCallback(() => null, [])
|
|
537
|
+
|
|
538
|
+
const Inner = React.useCallback((props: { match: RouteMatch }): any => {
|
|
539
|
+
if (props.match.store.state.status === 'error') {
|
|
540
|
+
throw props.match.store.state.error
|
|
516
541
|
}
|
|
517
542
|
|
|
518
|
-
|
|
519
|
-
|
|
543
|
+
if (props.match.store.state.status === 'success') {
|
|
544
|
+
return React.createElement(
|
|
545
|
+
(props.match.component as any) ??
|
|
546
|
+
router.options.defaultComponent ??
|
|
547
|
+
Outlet,
|
|
548
|
+
)
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (props.match.store.state.status === 'pending') {
|
|
552
|
+
throw props.match.__loadPromise
|
|
553
|
+
}
|
|
520
554
|
|
|
521
|
-
|
|
522
|
-
|
|
555
|
+
invariant(
|
|
556
|
+
false,
|
|
557
|
+
'Idle routeMatch status encountered during rendering! You should never see this. File an issue!',
|
|
558
|
+
)
|
|
559
|
+
}, [])
|
|
560
|
+
|
|
561
|
+
const PendingComponent = (match.pendingComponent ??
|
|
562
|
+
router.options.defaultPendingComponent ??
|
|
563
|
+
defaultPending) as any
|
|
564
|
+
|
|
565
|
+
const errorComponent =
|
|
566
|
+
match.errorComponent ?? router.options.defaultErrorComponent
|
|
523
567
|
|
|
524
568
|
return (
|
|
525
|
-
<
|
|
526
|
-
<
|
|
527
|
-
|
|
569
|
+
<matchesContext.Provider value={matches}>
|
|
570
|
+
<React.Suspense fallback={<PendingComponent />}>
|
|
571
|
+
<CatchBoundary
|
|
572
|
+
key={match.route.id}
|
|
573
|
+
errorComponent={errorComponent}
|
|
574
|
+
match={match as any}
|
|
575
|
+
>
|
|
576
|
+
<Inner match={match} />
|
|
577
|
+
</CatchBoundary>
|
|
578
|
+
</React.Suspense>
|
|
579
|
+
{/* Provide a suffix suspense boundary to make sure the router is
|
|
580
|
+
ready to be dehydrated on the server */}
|
|
581
|
+
{/* {router.options.ssrFooter && match.id === rootRouteId ? (
|
|
582
|
+
<React.Suspense fallback={null}>
|
|
583
|
+
{(() => {
|
|
584
|
+
if (router.store.pending) {
|
|
585
|
+
throw router.navigationPromise
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
return router.options.ssrFooter()
|
|
589
|
+
})()}
|
|
590
|
+
</React.Suspense>
|
|
591
|
+
) : null} */}
|
|
592
|
+
</matchesContext.Provider>
|
|
528
593
|
)
|
|
529
594
|
}
|
|
530
595
|
|
|
596
|
+
// This is the messiest thing ever... I'm either seriously tired (likely) or
|
|
597
|
+
// there has to be a better way to reset error boundaries when the
|
|
598
|
+
// router's location key changes.
|
|
599
|
+
|
|
531
600
|
class CatchBoundary extends React.Component<{
|
|
532
601
|
children: any
|
|
533
|
-
|
|
602
|
+
errorComponent: any
|
|
603
|
+
match: RouteMatch
|
|
534
604
|
}> {
|
|
535
605
|
state = {
|
|
536
606
|
error: false,
|
|
607
|
+
info: undefined,
|
|
537
608
|
}
|
|
538
609
|
componentDidCatch(error: any, info: any) {
|
|
610
|
+
console.error(`Error in route match: ${this.props.match.id}`)
|
|
539
611
|
console.error(error)
|
|
540
|
-
|
|
541
612
|
this.setState({
|
|
542
613
|
error,
|
|
543
614
|
info,
|
|
544
615
|
})
|
|
545
616
|
}
|
|
546
617
|
render() {
|
|
547
|
-
|
|
618
|
+
return (
|
|
619
|
+
<CatchBoundaryInner
|
|
620
|
+
{...this.props}
|
|
621
|
+
errorState={this.state}
|
|
622
|
+
reset={() => this.setState({})}
|
|
623
|
+
/>
|
|
624
|
+
)
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function CatchBoundaryInner(props: {
|
|
629
|
+
children: any
|
|
630
|
+
errorComponent: any
|
|
631
|
+
errorState: { error: unknown; info: any }
|
|
632
|
+
reset: () => void
|
|
633
|
+
}) {
|
|
634
|
+
const [activeErrorState, setActiveErrorState] = React.useState(
|
|
635
|
+
props.errorState,
|
|
636
|
+
)
|
|
637
|
+
const router = useRouter()
|
|
638
|
+
const errorComponent = props.errorComponent ?? DefaultErrorBoundary
|
|
639
|
+
const prevKeyRef = React.useRef('' as any)
|
|
640
|
+
|
|
641
|
+
React.useEffect(() => {
|
|
642
|
+
if (activeErrorState) {
|
|
643
|
+
if (router.store.state.currentLocation.key !== prevKeyRef.current) {
|
|
644
|
+
setActiveErrorState({} as any)
|
|
645
|
+
}
|
|
646
|
+
}
|
|
548
647
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
648
|
+
prevKeyRef.current = router.store.state.currentLocation.key
|
|
649
|
+
}, [activeErrorState, router.store.state.currentLocation.key])
|
|
650
|
+
|
|
651
|
+
React.useEffect(() => {
|
|
652
|
+
if (props.errorState.error) {
|
|
653
|
+
setActiveErrorState(props.errorState)
|
|
553
654
|
}
|
|
655
|
+
// props.reset()
|
|
656
|
+
}, [props.errorState.error])
|
|
554
657
|
|
|
555
|
-
|
|
658
|
+
if (props.errorState.error && activeErrorState.error) {
|
|
659
|
+
return React.createElement(errorComponent, activeErrorState)
|
|
556
660
|
}
|
|
661
|
+
|
|
662
|
+
return props.children
|
|
557
663
|
}
|
|
558
664
|
|
|
559
665
|
export function DefaultErrorBoundary({ error }: { error: any }) {
|
|
@@ -578,43 +684,33 @@ export function DefaultErrorBoundary({ error }: { error: any }) {
|
|
|
578
684
|
) : null}
|
|
579
685
|
</pre>
|
|
580
686
|
</div>
|
|
581
|
-
<div style={{ height: '1rem' }} />
|
|
582
|
-
<div
|
|
583
|
-
style={{
|
|
584
|
-
fontSize: '.8em',
|
|
585
|
-
borderLeft: '3px solid rgba(127, 127, 127, 1)',
|
|
586
|
-
paddingLeft: '.5rem',
|
|
587
|
-
opacity: 0.5,
|
|
588
|
-
}}
|
|
589
|
-
>
|
|
590
|
-
If you are the owner of this website, it's highly recommended that you
|
|
591
|
-
configure your own custom Catch/Error boundaries for the router. You can
|
|
592
|
-
optionally configure a boundary for each route.
|
|
593
|
-
</div>
|
|
594
687
|
</div>
|
|
595
688
|
)
|
|
596
689
|
}
|
|
597
690
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
React.useEffect(() => {
|
|
602
|
-
if (!when) return
|
|
603
|
-
|
|
604
|
-
let unblock = router.history.block((transition) => {
|
|
605
|
-
if (window.confirm(message)) {
|
|
606
|
-
unblock()
|
|
607
|
-
transition.retry()
|
|
608
|
-
} else {
|
|
609
|
-
router.location.pathname = window.location.pathname
|
|
610
|
-
}
|
|
611
|
-
})
|
|
691
|
+
// TODO: While we migrate away from the history package, these need to be disabled
|
|
692
|
+
// export function usePrompt(message: string, when: boolean | any): void {
|
|
693
|
+
// const router = useRouter()
|
|
612
694
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
695
|
+
// React.useEffect(() => {
|
|
696
|
+
// if (!when) return
|
|
697
|
+
|
|
698
|
+
// let unblock = router.getHistory().block((transition) => {
|
|
699
|
+
// if (window.confirm(message)) {
|
|
700
|
+
// unblock()
|
|
701
|
+
// transition.retry()
|
|
702
|
+
// } else {
|
|
703
|
+
// router.setStore((s) => {
|
|
704
|
+
// s.currentLocation.pathname = window.location.pathname
|
|
705
|
+
// })
|
|
706
|
+
// }
|
|
707
|
+
// })
|
|
708
|
+
|
|
709
|
+
// return unblock
|
|
710
|
+
// }, [when, message])
|
|
711
|
+
// }
|
|
616
712
|
|
|
617
|
-
export function Prompt({ message, when, children }: PromptProps) {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
}
|
|
713
|
+
// export function Prompt({ message, when, children }: PromptProps) {
|
|
714
|
+
// usePrompt(message, when ?? true)
|
|
715
|
+
// return (children ?? null) as ReactNode
|
|
716
|
+
// }
|