@tanstack/react-router 0.0.1-beta.235 → 0.0.1-beta.236

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.
Files changed (39) hide show
  1. package/build/cjs/Matches.js +34 -19
  2. package/build/cjs/Matches.js.map +1 -1
  3. package/build/cjs/RouterProvider.js +31 -26
  4. package/build/cjs/RouterProvider.js.map +1 -1
  5. package/build/cjs/index.js +1 -0
  6. package/build/cjs/index.js.map +1 -1
  7. package/build/cjs/route.js +13 -7
  8. package/build/cjs/route.js.map +1 -1
  9. package/build/cjs/router.js +49 -37
  10. package/build/cjs/router.js.map +1 -1
  11. package/build/cjs/useParams.js +7 -2
  12. package/build/cjs/useParams.js.map +1 -1
  13. package/build/cjs/useSearch.js +6 -1
  14. package/build/cjs/useSearch.js.map +1 -1
  15. package/build/cjs/utils.js +4 -1
  16. package/build/cjs/utils.js.map +1 -1
  17. package/build/esm/index.js +144 -94
  18. package/build/esm/index.js.map +1 -1
  19. package/build/stats-html.html +1 -1
  20. package/build/stats-react.json +574 -293
  21. package/build/types/Matches.d.ts +9 -3
  22. package/build/types/RouterProvider.d.ts +3 -0
  23. package/build/types/route.d.ts +30 -10
  24. package/build/types/router.d.ts +6 -3
  25. package/build/types/useParams.d.ts +3 -1
  26. package/build/types/useSearch.d.ts +3 -1
  27. package/build/types/utils.d.ts +3 -1
  28. package/build/umd/index.development.js +412 -97
  29. package/build/umd/index.development.js.map +1 -1
  30. package/build/umd/index.production.js +2 -2
  31. package/build/umd/index.production.js.map +1 -1
  32. package/package.json +4 -2
  33. package/src/Matches.tsx +72 -37
  34. package/src/RouterProvider.tsx +42 -31
  35. package/src/route.ts +37 -15
  36. package/src/router.ts +62 -44
  37. package/src/useParams.tsx +14 -4
  38. package/src/useSearch.tsx +11 -3
  39. package/src/utils.ts +20 -12
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
3
  "author": "Tanner Linsley",
4
- "version": "0.0.1-beta.235",
4
+ "version": "0.0.1-beta.236",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
7
  "homepage": "https://tanstack.com/router",
@@ -40,9 +40,11 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@babel/runtime": "^7.16.7",
43
+ "@tanstack/react-store": "^0.2.1",
44
+ "@tanstack/store": "^0.1.3",
43
45
  "tiny-invariant": "^1.3.1",
44
46
  "tiny-warning": "^1.0.3",
45
- "@tanstack/history": "0.0.1-beta.235"
47
+ "@tanstack/history": "0.0.1-beta.236"
46
48
  },
47
49
  "scripts": {
48
50
  "build": "rollup --config rollup.config.js"
package/src/Matches.tsx CHANGED
@@ -2,7 +2,7 @@ import * as React from 'react'
2
2
  import invariant from 'tiny-invariant'
3
3
  import warning from 'tiny-warning'
4
4
  import { CatchBoundary, ErrorComponent } from './CatchBoundary'
5
- import { useRouter } from './RouterProvider'
5
+ import { useRouter, useRouterState } from './RouterProvider'
6
6
  import { ResolveRelativePath, ToOptions } from './link'
7
7
  import { AnyRoute, ReactNode, rootRouteId } from './route'
8
8
  import {
@@ -47,10 +47,13 @@ export interface RouteMatch<
47
47
  export type AnyRouteMatch = RouteMatch<any>
48
48
 
49
49
  export function Matches() {
50
- const router = useRouter()
51
- const { matches } = router.state
52
- const locationKey = router.state.location.state.key
53
- const route = router.routesById[rootRouteId]!
50
+ const { routesById } = useRouter()
51
+ const routerState = useRouterState()
52
+ const matches = routerState.pendingMatches?.some((d) => d.showPending)
53
+ ? routerState.pendingMatches
54
+ : routerState.matches
55
+ const locationKey = useRouterState().location.state.key
56
+ const route = routesById[rootRouteId]!
54
57
 
55
58
  const errorComponent = React.useCallback(
56
59
  (props: any) => {
@@ -294,20 +297,28 @@ export function useMatch<
294
297
  TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
295
298
  TStrict extends boolean = true,
296
299
  TRouteMatchState = RouteMatch<TRouteTree, TFrom>,
300
+ TSelected = TRouteMatchState,
297
301
  >(
298
- opts: StrictOrFrom<TFrom>,
299
- ): TStrict extends true ? TRouteMatchState : TRouteMatchState | undefined {
300
- const router = useRouter()
302
+ opts: StrictOrFrom<TFrom> & {
303
+ select?: (match: TRouteMatchState) => TSelected
304
+ },
305
+ ): TStrict extends true ? TSelected : TSelected | undefined {
301
306
  const nearestMatch = React.useContext(matchesContext)[0]!
302
307
  const nearestMatchRouteId = nearestMatch?.routeId
303
308
 
304
- const matchRouteId = (() => {
305
- const match = opts?.from
306
- ? router.state.matches.find((d) => d.routeId === opts?.from)
307
- : router.state.matches.find((d) => d.id === nearestMatch.id)
309
+ const matchRouteId = useRouterState({
310
+ select: (state) => {
311
+ const matches = state.pendingMatches?.some((d) => d.showPending)
312
+ ? state.pendingMatches
313
+ : state.matches
308
314
 
309
- return match!.routeId
310
- })()
315
+ const match = opts?.from
316
+ ? matches.find((d) => d.routeId === opts?.from)
317
+ : matches.find((d) => d.id === nearestMatch.id)
318
+
319
+ return match!.routeId
320
+ },
321
+ })
311
322
 
312
323
  if (opts?.strict ?? true) {
313
324
  invariant(
@@ -322,32 +333,51 @@ export function useMatch<
322
333
  )
323
334
  }
324
335
 
325
- const matchSelection = (() => {
326
- const match = opts?.from
327
- ? router.state.matches.find((d) => d.routeId === opts?.from)
328
- : router.state.matches.find((d) => d.id === nearestMatch.id)
329
-
330
- invariant(
331
- match,
332
- `Could not find ${
333
- opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'
334
- }`,
335
- )
336
+ const matchSelection = useRouterState({
337
+ select: (state) => {
338
+ const matches = state.pendingMatches?.some((d) => d.showPending)
339
+ ? state.pendingMatches
340
+ : state.matches
341
+
342
+ const match = opts?.from
343
+ ? matches.find((d) => d.routeId === opts?.from)
344
+ : matches.find((d) => d.id === nearestMatch.id)
345
+
346
+ invariant(
347
+ match,
348
+ `Could not find ${
349
+ opts?.from
350
+ ? `an active match from "${opts.from}"`
351
+ : 'a nearest match!'
352
+ }`,
353
+ )
336
354
 
337
- return match
338
- })()
355
+ return opts?.select ? opts.select(match as any) : match
356
+ },
357
+ })
339
358
 
340
359
  return matchSelection as any
341
360
  }
342
361
 
343
362
  export const matchesContext = React.createContext<RouteMatch[]>(null!)
344
363
 
345
- export function useMatches(): RouteMatch[] {
346
- const router = useRouter()
364
+ export function useMatches<T = RouteMatch[]>(opts?: {
365
+ select?: (matches: RouteMatch[]) => T
366
+ }): T {
347
367
  const contextMatches = React.useContext(matchesContext)
348
- return router.state.matches.slice(
349
- router.state.matches.findIndex((d) => d.id === contextMatches[0]?.id),
350
- )
368
+
369
+ return useRouterState({
370
+ select: (state) => {
371
+ let matches = state.pendingMatches?.some((d) => d.showPending)
372
+ ? state.pendingMatches
373
+ : state.matches
374
+
375
+ matches = matches.slice(
376
+ matches.findIndex((d) => d.id === contextMatches[0]?.id),
377
+ )
378
+ return opts?.select ? opts.select(matches) : (matches as T)
379
+ },
380
+ })
351
381
  }
352
382
 
353
383
  export function useLoaderData<
@@ -358,10 +388,15 @@ export function useLoaderData<
358
388
  TRouteTree,
359
389
  TFrom
360
390
  >,
391
+ TSelected = TRouteMatch['loaderData'],
361
392
  >(
362
- opts: StrictOrFrom<TFrom>,
363
- ): TStrict extends true
364
- ? TRouteMatch['loaderData']
365
- : TRouteMatch['loaderData'] | undefined {
366
- return useMatch(opts)?.loaderData
393
+ opts: StrictOrFrom<TFrom> & {
394
+ select?: (match: TRouteMatch) => TSelected
395
+ },
396
+ ): TStrict extends true ? TSelected : TSelected | undefined {
397
+ const match = useMatch({ ...opts, select: undefined })!
398
+
399
+ return typeof opts.select === 'function'
400
+ ? opts.select(match?.loaderData)
401
+ : match?.loaderData
367
402
  }
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react'
2
2
  import warning from 'tiny-warning'
3
+ import { useStore } from '@tanstack/react-store'
3
4
  import { Matches } from './Matches'
4
5
  import {
5
6
  LinkInfo,
@@ -99,39 +100,33 @@ function RouterProviderInner<
99
100
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
100
101
  TDehydrated extends Record<string, any> = Record<string, any>,
101
102
  >({ router }: RouterProps<TRouteTree, TDehydrated>) {
102
- const [preState, setState] = React.useState(() => router.state)
103
103
  const [isTransitioning, startReactTransition] = React.useTransition()
104
- const isAnyTransitioning =
105
- isTransitioning || preState.matches.some((d) => d.status === 'pending')
106
-
107
- const state = React.useMemo<RouterState<TRouteTree>>(
108
- () => ({
109
- ...preState,
110
- status: isAnyTransitioning ? 'pending' : 'idle',
111
- location: isTransitioning ? router.latestLocation : preState.location,
112
- pendingMatches: router.pendingMatches,
113
- }),
114
- [preState, isTransitioning],
115
- )
116
104
 
117
- router.setState = setState
118
- router.state = state
119
105
  router.startReactTransition = startReactTransition
120
106
 
107
+ React.useEffect(() => {
108
+ if (isTransitioning) {
109
+ router.__store.setState((s) => ({
110
+ ...s,
111
+ isTransitioning,
112
+ }))
113
+ }
114
+ }, [isTransitioning])
115
+
121
116
  const tryLoad = () => {
122
- startReactTransition(() => {
123
- try {
124
- router.load()
125
- } catch (err) {
126
- console.error(err)
127
- }
128
- })
117
+ // startReactTransition(() => {
118
+ try {
119
+ router.load()
120
+ } catch (err) {
121
+ console.error(err)
122
+ }
123
+ // })
129
124
  }
130
125
 
131
126
  useLayoutEffect(() => {
132
127
  const unsub = router.history.subscribe(() => {
133
128
  router.latestLocation = router.parseLocation(router.latestLocation)
134
- if (state.location !== router.latestLocation) {
129
+ if (router.state.location !== router.latestLocation) {
135
130
  tryLoad()
136
131
  }
137
132
  })
@@ -143,7 +138,7 @@ function RouterProviderInner<
143
138
  state: true,
144
139
  })
145
140
 
146
- if (state.location.href !== nextLocation.href) {
141
+ if (router.state.location.href !== nextLocation.href) {
147
142
  router.commitLocation({ ...nextLocation, replace: true })
148
143
  }
149
144
 
@@ -153,21 +148,26 @@ function RouterProviderInner<
153
148
  }, [router.history])
154
149
 
155
150
  useLayoutEffect(() => {
156
- if (!isTransitioning && state.resolvedLocation !== state.location) {
151
+ if (
152
+ !isTransitioning &&
153
+ router.state.resolvedLocation !== router.state.location
154
+ ) {
157
155
  router.emit({
158
156
  type: 'onResolved',
159
- fromLocation: state.resolvedLocation,
160
- toLocation: state.location,
161
- pathChanged: state.location!.href !== state.resolvedLocation?.href,
157
+ fromLocation: router.state.resolvedLocation,
158
+ toLocation: router.state.location,
159
+ pathChanged:
160
+ router.state.location!.href !== router.state.resolvedLocation?.href,
162
161
  })
163
162
  router.pendingMatches = []
164
163
 
165
- setState((s) => ({
164
+ router.__store.setState((s) => ({
166
165
  ...s,
166
+ isTransitioning: false,
167
167
  resolvedLocation: s.location,
168
168
  }))
169
169
  }
170
- })
170
+ }, [isTransitioning])
171
171
 
172
172
  useLayoutEffect(() => {
173
173
  if (!window.__TSR_DEHYDRATED__) {
@@ -186,7 +186,18 @@ export function getRouteMatch<TRouteTree extends AnyRoute>(
186
186
  state: RouterState<TRouteTree>,
187
187
  id: string,
188
188
  ): undefined | RouteMatch<TRouteTree> {
189
- return [...state.pendingMatches, ...state.matches].find((d) => d.id === id)
189
+ return [...(state.pendingMatches ?? []), ...state.matches].find(
190
+ (d) => d.id === id,
191
+ )
192
+ }
193
+
194
+ export function useRouterState<
195
+ TSelected = RouterState<RegisteredRouter['routeTree']>,
196
+ >(opts?: {
197
+ select: (state: RouterState<RegisteredRouter['routeTree']>) => TSelected
198
+ }): TSelected {
199
+ const router = useRouter()
200
+ return useStore(router.__store, opts?.select as any)
190
201
  }
191
202
 
192
203
  export type RouterProps<
package/src/route.ts CHANGED
@@ -590,22 +590,34 @@ export class Route<
590
590
  // replaced by a framework specific implementation if necessary
591
591
  }
592
592
 
593
- useMatch = (): TAllContext => {
594
- return useMatch({ from: this.id }) as any
593
+ useMatch = <TSelected = TAllContext>(opts?: {
594
+ select?: (search: TAllContext) => TSelected
595
+ }): TSelected => {
596
+ return useMatch({ ...opts, from: this.id }) as any
595
597
  }
596
- useRouteContext = (): TAllContext => {
598
+ useRouteContext = <TSelected = TAllContext>(opts?: {
599
+ select?: (search: TAllContext) => TSelected
600
+ }): TSelected => {
597
601
  return useMatch({
602
+ ...opts,
598
603
  from: this.id,
599
- } as any).context
604
+ select: (d: any) => (opts?.select ? opts.select(d.context) : d.context),
605
+ } as any)
600
606
  }
601
- useSearch = (): TFullSearchSchema => {
602
- return useSearch({ from: this.id } as any)
607
+ useSearch = <TSelected = TFullSearchSchema>(opts?: {
608
+ select?: (search: TFullSearchSchema) => TSelected
609
+ }): TSelected => {
610
+ return useSearch({ ...opts, from: this.id } as any)
603
611
  }
604
- useParams = (): TAllParams => {
605
- return useParams({ from: this.id } as any)
612
+ useParams = <TSelected = TAllParams>(opts?: {
613
+ select?: (search: TAllParams) => TSelected
614
+ }): TSelected => {
615
+ return useParams({ ...opts, from: this.id } as any)
606
616
  }
607
- useLoaderData = (): TLoaderData => {
608
- return useLoaderData({ from: this.id } as any) as any
617
+ useLoaderData = <TSelected = TLoaderData>(opts?: {
618
+ select?: (search: TLoaderData) => TSelected
619
+ }): TSelected => {
620
+ return useLoaderData({ ...opts, from: this.id } as any) as any
609
621
  }
610
622
  }
611
623
 
@@ -756,11 +768,21 @@ export type RouteProps<
756
768
  TAllContext extends Record<string, any> = AnyContext,
757
769
  TLoaderData extends any = unknown,
758
770
  > = {
759
- useMatch: () => TAllContext
760
- useRouteContext: () => TAllContext
761
- useSearch: () => TFullSearchSchema
762
- useParams: () => TAllParams
763
- useLoaderData: () => TLoaderData
771
+ useMatch: <TSelected = TAllContext>(opts?: {
772
+ select?: (search: TAllContext) => TSelected
773
+ }) => TSelected
774
+ useRouteContext: <TSelected = TAllContext>(opts?: {
775
+ select?: (search: TAllContext) => TSelected
776
+ }) => TSelected
777
+ useSearch: <TSelected = TFullSearchSchema>(opts?: {
778
+ select?: (search: TFullSearchSchema) => TSelected
779
+ }) => TSelected
780
+ useParams: <TSelected = TAllParams>(opts?: {
781
+ select?: (search: TAllParams) => TSelected
782
+ }) => TSelected
783
+ useLoaderData: <TSelected = TLoaderData>(opts?: {
784
+ select?: (search: TLoaderData) => TSelected
785
+ }) => TSelected
764
786
  }
765
787
 
766
788
  export type ErrorRouteProps<
package/src/router.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  createBrowserHistory,
6
6
  createMemoryHistory,
7
7
  } from '@tanstack/history'
8
+ import { Store } from '@tanstack/store'
8
9
 
9
10
  //
10
11
 
@@ -29,7 +30,6 @@ import {
29
30
  functionalUpdate,
30
31
  last,
31
32
  pick,
32
- PickAsPartial,
33
33
  } from './utils'
34
34
  import {
35
35
  ErrorRouteComponent,
@@ -134,8 +134,10 @@ export interface RouterOptions<
134
134
 
135
135
  export interface RouterState<TRouteTree extends AnyRoute = AnyRoute> {
136
136
  status: 'pending' | 'idle'
137
+ isLoading: boolean
138
+ isTransitioning: boolean
137
139
  matches: RouteMatch<TRouteTree>[]
138
- pendingMatches: RouteMatch<TRouteTree>[]
140
+ pendingMatches?: RouteMatch<TRouteTree>[]
139
141
  location: ParsedLocation<FullSearchSchema<TRouteTree>>
140
142
  resolvedLocation: ParsedLocation<FullSearchSchema<TRouteTree>>
141
143
  lastUpdated: number
@@ -236,7 +238,7 @@ export class Router<
236
238
  dehydratedData?: TDehydrated
237
239
 
238
240
  // Must build in constructor
239
- state!: RouterState<TRouteTree>
241
+ __store!: Store<RouterState<TRouteTree>>
240
242
  options!: PickAsRequired<
241
243
  RouterOptions<TRouteTree, TDehydrated>,
242
244
  'stringifySearch' | 'parseSearch' | 'context'
@@ -265,11 +267,6 @@ export class Router<
265
267
  // by the router provider once rendered. We provide these so that the
266
268
  // router can be used in a non-react environment if necessary
267
269
  startReactTransition: (fn: () => void) => void = (fn) => fn()
268
- setState: (updater: NonNullableUpdater<RouterState<TRouteTree>>) => void = (
269
- updater,
270
- ) => {
271
- this.state = functionalUpdate(updater, this.state)
272
- }
273
270
 
274
271
  update = (newOptions: RouterConstructorOptions<TRouteTree, TDehydrated>) => {
275
272
  this.options = {
@@ -296,11 +293,25 @@ export class Router<
296
293
  this.buildRouteTree()
297
294
  }
298
295
 
299
- if (!this.state) {
300
- this.state = getInitialRouterState(this.latestLocation)
296
+ if (!this.__store) {
297
+ this.__store = new Store(getInitialRouterState(this.latestLocation), {
298
+ onUpdate: () => {
299
+ this.__store.state = {
300
+ ...this.state,
301
+ status:
302
+ this.state.isTransitioning || this.state.isLoading
303
+ ? 'pending'
304
+ : 'idle',
305
+ }
306
+ },
307
+ })
301
308
  }
302
309
  }
303
310
 
311
+ get state() {
312
+ return this.__store.state
313
+ }
314
+
304
315
  buildRouteTree = () => {
305
316
  this.routesById = {} as RoutesById<TRouteTree>
306
317
  this.routesByPath = {} as RoutesByPath<TRouteTree>
@@ -654,7 +665,7 @@ export class Router<
654
665
  }
655
666
 
656
667
  cancelMatches = () => {
657
- this.state.matches.forEach((match) => {
668
+ this.state.pendingMatches?.forEach((match) => {
658
669
  this.cancelMatch(match.id)
659
670
  })
660
671
  }
@@ -948,6 +959,15 @@ export class Router<
948
959
  let latestPromise
949
960
  let firstBadMatchIndex: number | undefined
950
961
 
962
+ const updatePendingMatch = (match: AnyRouteMatch) => {
963
+ this.__store.setState((s) => ({
964
+ ...s,
965
+ pendingMatches: s.pendingMatches?.map((d) =>
966
+ d.id === match.id ? match : d,
967
+ ),
968
+ }))
969
+ }
970
+
951
971
  // Check each match middleware to see if the route can be accessed
952
972
  try {
953
973
  for (let [index, match] of matches.entries()) {
@@ -1149,13 +1169,12 @@ export class Router<
1149
1169
  }
1150
1170
 
1151
1171
  if (!preload) {
1152
- this.setState((s) => ({
1153
- ...s,
1154
- matches: s.matches.map((d) => (d.id === match.id ? match : d)),
1155
- }))
1172
+ updatePendingMatch(match)
1156
1173
  }
1157
1174
 
1158
1175
  let didShowPending = false
1176
+ const pendingMinMs =
1177
+ route.options.pendingMinMs ?? this.options.defaultPendingMinMs
1159
1178
 
1160
1179
  await new Promise<void>(async (resolve) => {
1161
1180
  // If the route has a pending component and a pendingMs option,
@@ -1170,12 +1189,7 @@ export class Router<
1170
1189
  showPending: true,
1171
1190
  }
1172
1191
 
1173
- this.setState((s) => ({
1174
- ...s,
1175
- matches: s.matches.map((d) =>
1176
- d.id === match.id ? match : d,
1177
- ),
1178
- }))
1192
+ updatePendingMatch(match)
1179
1193
  resolve()
1180
1194
  })
1181
1195
  }
@@ -1184,9 +1198,6 @@ export class Router<
1184
1198
  const loaderData = await loadPromise
1185
1199
  if ((latestPromise = checkLatest())) return await latestPromise
1186
1200
 
1187
- const pendingMinMs =
1188
- route.options.pendingMinMs ?? this.options.defaultPendingMinMs
1189
-
1190
1201
  if (didShowPending && pendingMinMs) {
1191
1202
  await new Promise((r) => setTimeout(r, pendingMinMs))
1192
1203
  }
@@ -1220,13 +1231,22 @@ export class Router<
1220
1231
  isFetching: false,
1221
1232
  updatedAt: Date.now(),
1222
1233
  }
1234
+ } finally {
1235
+ // If we showed the pending component, that means
1236
+ // we already moved the pendingMatches to the matches
1237
+ // state, so we need to update that specific match
1238
+ if (didShowPending && pendingMinMs && match.showPending) {
1239
+ this.__store.setState((s) => ({
1240
+ ...s,
1241
+ matches: s.matches?.map((d) =>
1242
+ d.id === match.id ? match : d,
1243
+ ),
1244
+ }))
1245
+ }
1223
1246
  }
1224
1247
 
1225
1248
  if (!preload) {
1226
- this.setState((s) => ({
1227
- ...s,
1228
- matches: s.matches.map((d) => (d.id === match.id ? match : d)),
1229
- }))
1249
+ updatePendingMatch(match)
1230
1250
  }
1231
1251
 
1232
1252
  resolve()
@@ -1262,7 +1282,7 @@ export class Router<
1262
1282
  })
1263
1283
 
1264
1284
  // Match the routes
1265
- let matches: RouteMatch<any, any>[] = this.matchRoutes(
1285
+ let pendingMatches: RouteMatch<any, any>[] = this.matchRoutes(
1266
1286
  next.pathname,
1267
1287
  next.search,
1268
1288
  {
@@ -1270,23 +1290,21 @@ export class Router<
1270
1290
  },
1271
1291
  )
1272
1292
 
1273
- this.pendingMatches = matches
1274
-
1275
1293
  const previousMatches = this.state.matches
1276
1294
 
1277
1295
  // Ingest the new matches
1278
- this.setState((s) => ({
1296
+ this.__store.setState((s) => ({
1279
1297
  ...s,
1280
- // status: 'pending',
1298
+ isLoading: true,
1281
1299
  location: next,
1282
- matches,
1300
+ pendingMatches,
1283
1301
  }))
1284
1302
 
1285
1303
  try {
1286
1304
  try {
1287
1305
  // Load the matches
1288
1306
  await this.loadMatches({
1289
- matches,
1307
+ matches: pendingMatches,
1290
1308
  checkLatest: () => this.checkLatest(promise),
1291
1309
  invalidate: opts?.invalidate,
1292
1310
  })
@@ -1310,12 +1328,12 @@ export class Router<
1310
1328
  this.pendingMatches.includes(id),
1311
1329
  )
1312
1330
 
1313
- // setState((s) => ({
1314
- // ...s,
1315
- // status: 'idle',
1316
- // resolvedLocation: s.location,
1317
- // matches,
1318
- // }))
1331
+ this.__store.setState((s) => ({
1332
+ ...s,
1333
+ isLoading: false,
1334
+ matches: pendingMatches,
1335
+ pendingMatches: undefined,
1336
+ }))
1319
1337
 
1320
1338
  //
1321
1339
  ;(
@@ -1517,8 +1535,6 @@ export class Router<
1517
1535
  ? this.latestLocation
1518
1536
  : this.state.resolvedLocation
1519
1537
 
1520
- // const baseLocation = state.resolvedLocation
1521
-
1522
1538
  if (!baseLocation) {
1523
1539
  return false
1524
1540
  }
@@ -1633,7 +1649,7 @@ export class Router<
1633
1649
  return match
1634
1650
  })
1635
1651
 
1636
- this.setState((s) => {
1652
+ this.__store.setState((s) => {
1637
1653
  return {
1638
1654
  ...s,
1639
1655
  matches: matches as any,
@@ -1672,6 +1688,8 @@ export function getInitialRouterState(
1672
1688
  location: ParsedLocation,
1673
1689
  ): RouterState<any> {
1674
1690
  return {
1691
+ isLoading: false,
1692
+ isTransitioning: false,
1675
1693
  status: 'idle',
1676
1694
  resolvedLocation: location,
1677
1695
  location,
package/src/useParams.tsx CHANGED
@@ -2,14 +2,24 @@ import { AnyRoute } from './route'
2
2
  import { RouteIds, RouteById, AllParams } from './routeInfo'
3
3
  import { RegisteredRouter } from './router'
4
4
  import { last } from './utils'
5
- import { useRouter } from './RouterProvider'
5
+ import { useRouterState } from './RouterProvider'
6
6
  import { StrictOrFrom } from './utils'
7
7
 
8
8
  export function useParams<
9
9
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
10
10
  TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
11
+ TDefaultSelected = AllParams<TRouteTree> &
12
+ RouteById<TRouteTree, TFrom>['types']['allParams'],
13
+ TSelected = TDefaultSelected,
11
14
  >(
12
- _opts: StrictOrFrom<TFrom>,
13
- ): AllParams<TRouteTree> & RouteById<TRouteTree, TFrom>['types']['allParams'] {
14
- return (last(useRouter().state.matches) as any)?.params
15
+ opts: StrictOrFrom<TFrom> & {
16
+ select?: (search: TDefaultSelected) => TSelected
17
+ },
18
+ ): TSelected {
19
+ return useRouterState({
20
+ select: (state: any) => {
21
+ const params = (last(state.matches) as any)?.params
22
+ return opts?.select ? opts.select(params) : params
23
+ },
24
+ })
15
25
  }
package/src/useSearch.tsx CHANGED
@@ -10,8 +10,16 @@ export function useSearch<
10
10
  TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
11
11
  TStrict extends boolean = true,
12
12
  TSearch = RouteById<TRouteTree, TFrom>['types']['fullSearchSchema'],
13
+ TSelected = TSearch,
13
14
  >(
14
- opts: StrictOrFrom<TFrom>,
15
- ): TStrict extends true ? TSearch : TSearch | undefined {
16
- return useMatch(opts).search
15
+ opts: StrictOrFrom<TFrom> & {
16
+ select?: (search: TSearch) => TSelected
17
+ },
18
+ ): TStrict extends true ? TSelected : TSelected | undefined {
19
+ return useMatch({
20
+ ...(opts as any),
21
+ select: (match: RouteMatch) => {
22
+ return opts?.select ? opts.select(match.search as TSearch) : match.search
23
+ },
24
+ })
17
25
  }