@tanstack/react-router 1.8.3 → 1.9.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
- "version": "1.8.3",
3
+ "version": "1.9.0",
4
4
  "description": "",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
package/src/Matches.tsx CHANGED
@@ -7,6 +7,8 @@ import { useRouter } from './useRouter'
7
7
  import { ResolveRelativePath, ToOptions } from './link'
8
8
  import { AnyRoute, ReactNode, RootSearchSchema } from './route'
9
9
  import {
10
+ AllParams,
11
+ FullSearchSchema,
10
12
  ParseRoute,
11
13
  RouteById,
12
14
  RouteByPath,
@@ -14,18 +16,21 @@ import {
14
16
  RoutePaths,
15
17
  } from './routeInfo'
16
18
  import { RegisteredRouter, RouterState } from './router'
17
- import { DeepOptional, NoInfer, StrictOrFrom, pick } from './utils'
19
+ import { DeepOptional, Expand, NoInfer, StrictOrFrom, pick } from './utils'
18
20
 
19
21
  export const matchContext = React.createContext<string | undefined>(undefined)
20
22
 
21
23
  export interface RouteMatch<
22
- TRouteTree extends AnyRoute = AnyRoute,
24
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
23
25
  TRouteId extends RouteIds<TRouteTree> = ParseRoute<TRouteTree>['id'],
26
+ TReturnIntersection extends boolean = false,
24
27
  > {
25
28
  id: string
26
29
  routeId: TRouteId
27
30
  pathname: string
28
- params: RouteById<TRouteTree, TRouteId>['types']['allParams']
31
+ params: TReturnIntersection extends false
32
+ ? RouteById<TRouteTree, TRouteId>['types']['allParams']
33
+ : Expand<Partial<AllParams<TRouteTree>>>
29
34
  status: 'pending' | 'success' | 'error'
30
35
  isFetching: boolean
31
36
  showPending: boolean
@@ -37,10 +42,14 @@ export interface RouteMatch<
37
42
  loaderData?: RouteById<TRouteTree, TRouteId>['types']['loaderData']
38
43
  routeContext: RouteById<TRouteTree, TRouteId>['types']['routeContext']
39
44
  context: RouteById<TRouteTree, TRouteId>['types']['allContext']
40
- search: Exclude<
41
- RouteById<TRouteTree, TRouteId>['types']['fullSearchSchema'],
42
- RootSearchSchema
43
- >
45
+ search: TReturnIntersection extends false
46
+ ? Exclude<
47
+ RouteById<TRouteTree, TRouteId>['types']['fullSearchSchema'],
48
+ RootSearchSchema
49
+ >
50
+ : Expand<
51
+ Partial<Omit<FullSearchSchema<TRouteTree>, keyof RootSearchSchema>>
52
+ >
44
53
  fetchCount: number
45
54
  abortController: AbortController
46
55
  cause: 'preload' | 'enter' | 'stay'
@@ -298,7 +307,9 @@ export function MatchRoute<
298
307
  return !!params ? props.children : null
299
308
  }
300
309
 
301
- export function getRenderedMatches(state: RouterState) {
310
+ export function getRenderedMatches<
311
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
312
+ >(state: RouterState<TRouteTree>) {
302
313
  return state.pendingMatches?.some((d) => d.showPending)
303
314
  ? state.pendingMatches
304
315
  : state.matches
@@ -307,10 +318,11 @@ export function getRenderedMatches(state: RouterState) {
307
318
  export function useMatch<
308
319
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
309
320
  TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
310
- TRouteMatchState = RouteMatch<TRouteTree, TFrom>,
321
+ TReturnIntersection extends boolean = false,
322
+ TRouteMatchState = RouteMatch<TRouteTree, TFrom, TReturnIntersection>,
311
323
  TSelected = TRouteMatchState,
312
324
  >(
313
- opts: StrictOrFrom<TFrom> & {
325
+ opts: StrictOrFrom<TFrom, TReturnIntersection> & {
314
326
  select?: (match: TRouteMatchState) => TSelected
315
327
  },
316
328
  ): TSelected {
@@ -364,26 +376,44 @@ export function useMatch<
364
376
  return matchSelection as any
365
377
  }
366
378
 
367
- export function useMatches<T = RouteMatch[]>(opts?: {
368
- select?: (matches: RouteMatch[]) => T
379
+ export function useMatches<
380
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
381
+ TRouteId extends RouteIds<TRouteTree> = ParseRoute<TRouteTree>['id'],
382
+ TReturnIntersection extends boolean = false,
383
+ TRouteMatch = RouteMatch<TRouteTree, TRouteId, TReturnIntersection>,
384
+ T = TRouteMatch[],
385
+ >(opts?: {
386
+ select?: (matches: TRouteMatch[]) => T
387
+ experimental_returnIntersection?: TReturnIntersection
369
388
  }): T {
370
389
  return useRouterState({
371
390
  select: (state) => {
372
- let matches = getRenderedMatches(state)
373
- return opts?.select ? opts.select(matches) : (matches as T)
391
+ const matches = getRenderedMatches(state)
392
+ return opts?.select
393
+ ? opts.select(matches as TRouteMatch[])
394
+ : (matches as T)
374
395
  },
375
396
  })
376
397
  }
377
398
 
378
- export function useParentMatches<T = RouteMatch[]>(opts?: {
379
- select?: (matches: RouteMatch[]) => T
399
+ export function useParentMatches<
400
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
401
+ TRouteId extends RouteIds<TRouteTree> = ParseRoute<TRouteTree>['id'],
402
+ TReturnIntersection extends boolean = false,
403
+ TRouteMatch = RouteMatch<TRouteTree, TRouteId, TReturnIntersection>,
404
+ T = TRouteMatch[],
405
+ >(opts?: {
406
+ select?: (matches: TRouteMatch[]) => T
407
+ experimental_returnIntersection?: TReturnIntersection
380
408
  }): T {
381
409
  const contextMatchId = React.useContext(matchContext)
382
410
 
383
411
  return useMatches({
384
412
  select: (matches) => {
385
413
  matches = matches.slice(matches.findIndex((d) => d.id === contextMatchId))
386
- return opts?.select ? opts.select(matches) : (matches as T)
414
+ return opts?.select
415
+ ? opts.select(matches as TRouteMatch[])
416
+ : (matches as T)
387
417
  },
388
418
  })
389
419
  }
@@ -88,8 +88,8 @@ export function RouterProvider<
88
88
  }
89
89
 
90
90
  function Transitioner() {
91
- const mountLoadCount = React.useRef(0)
92
91
  const router = useRouter()
92
+ const mountLoadForRouter = React.useRef({ router, mounted: false })
93
93
  const routerState = useRouterState({
94
94
  select: (s) =>
95
95
  pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning']),
@@ -192,11 +192,16 @@ function Transitioner() {
192
192
  ])
193
193
 
194
194
  useLayoutEffect(() => {
195
- if (!window.__TSR_DEHYDRATED__ && !mountLoadCount.current) {
196
- mountLoadCount.current++
197
- tryLoad()
195
+ if (window.__TSR_DEHYDRATED__) return
196
+ if (
197
+ mountLoadForRouter.current.router === router &&
198
+ mountLoadForRouter.current.mounted
199
+ ) {
200
+ return
198
201
  }
199
- }, [])
202
+ mountLoadForRouter.current = { router, mounted: true }
203
+ tryLoad()
204
+ }, [router])
200
205
 
201
206
  return null
202
207
  }
package/src/route.ts CHANGED
@@ -236,15 +236,6 @@ export type SearchSchemaValidatorFn<TInput, TReturn> = (
236
236
  searchObj: TInput,
237
237
  ) => TReturn
238
238
 
239
- export type DefinedPathParamWarning =
240
- 'Path params cannot be redefined by child routes!'
241
-
242
- export type ParentParams<TParentParams> = AnyPathParams extends TParentParams
243
- ? {}
244
- : {
245
- [Key in keyof TParentParams]?: DefinedPathParamWarning
246
- }
247
-
248
239
  export type RouteLoaderFn<
249
240
  TAllParams = {},
250
241
  TLoaderDeps extends Record<string, any> = {},
package/src/useParams.tsx CHANGED
@@ -1,7 +1,7 @@
1
1
  import { AnyRoute } from './route'
2
- import { RouteIds, RouteById } from './routeInfo'
2
+ import { RouteIds, RouteById, AllParams } from './routeInfo'
3
3
  import { RegisteredRouter } from './router'
4
- import { last } from './utils'
4
+ import { Expand, last } from './utils'
5
5
  import { useRouterState } from './useRouterState'
6
6
  import { StrictOrFrom } from './utils'
7
7
  import { getRenderedMatches } from './Matches'
@@ -9,10 +9,13 @@ import { getRenderedMatches } from './Matches'
9
9
  export function useParams<
10
10
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
11
11
  TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
12
- TParams = RouteById<TRouteTree, TFrom>['types']['allParams'],
12
+ TReturnIntersection extends boolean = false,
13
+ TParams = TReturnIntersection extends false
14
+ ? RouteById<TRouteTree, TFrom>['types']['allParams']
15
+ : Expand<Partial<AllParams<TRouteTree>>>,
13
16
  TSelected = TParams,
14
17
  >(
15
- opts: StrictOrFrom<TFrom> & {
18
+ opts: StrictOrFrom<TFrom, TReturnIntersection> & {
16
19
  select?: (params: TParams) => TSelected
17
20
  },
18
21
  ): TSelected {
package/src/useSearch.tsx CHANGED
@@ -1,20 +1,25 @@
1
1
  import { AnyRoute, RootSearchSchema } from './route'
2
- import { RouteIds, RouteById } from './routeInfo'
2
+ import { RouteIds, RouteById, FullSearchSchema } from './routeInfo'
3
3
  import { RegisteredRouter } from './router'
4
4
  import { RouteMatch } from './Matches'
5
5
  import { useMatch } from './Matches'
6
- import { StrictOrFrom } from './utils'
6
+ import { Expand, StrictOrFrom } from './utils'
7
7
 
8
8
  export function useSearch<
9
9
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
10
10
  TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
11
- TSearch = Exclude<
12
- RouteById<TRouteTree, TFrom>['types']['fullSearchSchema'],
13
- RootSearchSchema
14
- >,
11
+ TReturnIntersection extends boolean = false,
12
+ TSearch = TReturnIntersection extends false
13
+ ? Exclude<
14
+ RouteById<TRouteTree, TFrom>['types']['fullSearchSchema'],
15
+ RootSearchSchema
16
+ >
17
+ : Expand<
18
+ Partial<Omit<FullSearchSchema<TRouteTree>, keyof RootSearchSchema>>
19
+ >,
15
20
  TSelected = TSearch,
16
21
  >(
17
- opts: StrictOrFrom<TFrom> & {
22
+ opts: StrictOrFrom<TFrom, TReturnIntersection> & {
18
23
  select?: (search: TSearch) => TSelected
19
24
  },
20
25
  ): TSelected {
package/src/utils.ts CHANGED
@@ -300,7 +300,7 @@ export type StringLiteral<T> = T extends string
300
300
  : T
301
301
  : never
302
302
 
303
- export type StrictOrFrom<TFrom> =
303
+ export type StrictOrFrom<TFrom, TReturnIntersection extends boolean = false> =
304
304
  | {
305
305
  from: StringLiteral<TFrom> | TFrom
306
306
  strict?: true
@@ -308,6 +308,7 @@ export type StrictOrFrom<TFrom> =
308
308
  | {
309
309
  from?: never
310
310
  strict: false
311
+ experimental_returnIntersection?: TReturnIntersection
311
312
  }
312
313
 
313
314
  export const useLayoutEffect =