@tanstack/router-core 0.0.1-beta.34 → 0.0.1-beta.36

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@tanstack/router-core",
3
3
  "author": "Tanner Linsley",
4
- "version": "0.0.1-beta.34",
4
+ "version": "0.0.1-beta.36",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
7
  "homepage": "https://tanstack.com/router",
package/src/path.ts CHANGED
@@ -165,10 +165,10 @@ export function matchByPath(
165
165
  from: string,
166
166
  matchLocation: Pick<MatchLocation, 'to' | 'caseSensitive' | 'fuzzy'>,
167
167
  ): Record<string, string> | undefined {
168
- if (basepath && !from.startsWith(basepath)) {
168
+ if (!from.startsWith(basepath)) {
169
169
  return undefined
170
170
  }
171
- from = from.startsWith(basepath) ? from.substring(basepath.length) : from
171
+ from = basepath != '/' ? from.substring(basepath.length) : from
172
172
  const baseSegments = parsePathname(from)
173
173
  const to = `${matchLocation.to ?? '*'}`
174
174
  const routeSegments = parsePathname(to)
package/src/route.ts CHANGED
@@ -140,7 +140,6 @@ export function createRoute<
140
140
  actionState.status = 'success'
141
141
  return res
142
142
  } catch (err) {
143
- console.log('tanner')
144
143
  console.error(err)
145
144
  actionState.error = err
146
145
  actionState.status = 'error'
@@ -123,12 +123,17 @@ export type RouteOptions<
123
123
  // An asynchronous function made available to the route for performing asynchronous or mutative actions that
124
124
  // might invalidate the route's data.
125
125
  action?: ActionFn<TActionPayload, TActionResponse>
126
- // This async function is called before a route is loaded. If an error is thrown, the navigation is cancelled.
127
- // If you want to redirect instead, throw a call to the `router.navigate()` function
126
+ // This async function is called before a route is loaded.
127
+ // If an error is thrown here, the route's loader will not be called.
128
+ // If thrown during a navigation, the navigation will be cancelled and the error will be passed to the `onLoadError` function.
129
+ // If thrown during a preload event, the error will be logged to the console.
128
130
  beforeLoad?: (opts: {
129
131
  router: Router<any, any, unknown>
130
132
  match: RouteMatch
131
133
  }) => Promise<void> | void
134
+ // This function will be called if the route's loader throws an error **during an attempted navigation**.
135
+ // If you want to redirect due to an error, call `router.navigate()` from within this function.
136
+ onLoadError?: (err: any) => void
132
137
  // This function is called
133
138
  // when moving from an inactive state to an active one. Likewise, when moving from
134
139
  // an active to an inactive state, the return function (if provided) is called.
package/src/routeMatch.ts CHANGED
@@ -111,7 +111,7 @@ export function createRouteMatch<
111
111
  validate: () => {
112
112
  // Validate the search params and stabilize them
113
113
  const parentSearch =
114
- routeMatch.parentMatch?.search ?? router.__location.search
114
+ routeMatch.parentMatch?.search ?? router.state.currentLocation.search
115
115
 
116
116
  try {
117
117
  const prevSearch = routeMatch.routeSearch
@@ -180,7 +180,9 @@ export function createRouteMatch<
180
180
  if (loaderOpts?.preload && minMaxAge > 0) {
181
181
  // If the match is currently active, don't preload it
182
182
  if (
183
- router.state.matches.find((d) => d.matchId === routeMatch.matchId)
183
+ router.state.currentMatches.find(
184
+ (d) => d.matchId === routeMatch.matchId,
185
+ )
184
186
  ) {
185
187
  return
186
188
  }
package/src/router.ts CHANGED
@@ -201,21 +201,18 @@ export interface RouterState<
201
201
  TState extends LocationState = LocationState,
202
202
  > {
203
203
  status: 'idle' | 'loading'
204
- location: Location<TSearchObj, TState>
205
- matches: RouteMatch[]
204
+ latestLocation: Location<TSearchObj, TState>
205
+ currentMatches: RouteMatch[]
206
+ currentLocation: Location<TSearchObj, TState>
207
+ pendingMatches?: RouteMatch[]
208
+ pendingLocation?: Location<TSearchObj, TState>
206
209
  lastUpdated: number
207
210
  actions: Record<string, Action>
208
211
  loaders: Record<string, Loader>
209
- pending?: PendingState
210
212
  isFetching: boolean
211
213
  isPreloading: boolean
212
214
  }
213
215
 
214
- export interface PendingState {
215
- location: Location
216
- matches: RouteMatch[]
217
- }
218
-
219
216
  type Listener = (router: Router<any, any, any>) => void
220
217
 
221
218
  export type ListenerFn = () => void
@@ -259,13 +256,13 @@ type LinkCurrentTargetElement = {
259
256
  export interface DehydratedRouterState
260
257
  extends Pick<
261
258
  RouterState,
262
- 'status' | 'location' | 'lastUpdated' | 'location'
259
+ 'status' | 'latestLocation' | 'currentLocation' | 'lastUpdated'
263
260
  > {
264
- matches: DehydratedRouteMatch[]
261
+ currentMatches: DehydratedRouteMatch[]
265
262
  }
266
263
 
267
264
  export interface DehydratedRouter<TRouterContext = unknown> {
268
- location: Router['__location']
265
+ // location: Router['__location']
269
266
  state: DehydratedRouterState
270
267
  context: TRouterContext
271
268
  }
@@ -304,7 +301,7 @@ export interface Router<
304
301
  basepath: string
305
302
  // Internal:
306
303
  listeners: Listener[]
307
- __location: Location<TAllRouteInfo['fullSearchSchema']>
304
+ // __location: Location<TAllRouteInfo['fullSearchSchema']>
308
305
  navigateTimeout?: Timeout
309
306
  nextAction?: 'push' | 'replace'
310
307
  state: RouterState<TAllRouteInfo['fullSearchSchema']>
@@ -403,8 +400,9 @@ const createDefaultHistory = () =>
403
400
  function getInitialRouterState(): RouterState {
404
401
  return {
405
402
  status: 'idle',
406
- location: null!,
407
- matches: [],
403
+ latestLocation: null!,
404
+ currentLocation: null!,
405
+ currentMatches: [],
408
406
  actions: {},
409
407
  loaders: {},
410
408
  lastUpdated: Date.now(),
@@ -444,7 +442,6 @@ export function createRouter<
444
442
  basepath: '',
445
443
  routeTree: undefined!,
446
444
  routesById: {} as any,
447
- __location: undefined!,
448
445
  //
449
446
  resolveNavigation: () => {},
450
447
  matchCache: {},
@@ -466,12 +463,14 @@ export function createRouter<
466
463
  notify: (): void => {
467
464
  const isFetching =
468
465
  router.state.status === 'loading' ||
469
- router.state.matches.some((d) => d.isFetching)
466
+ router.state.currentMatches.some((d) => d.isFetching)
470
467
 
471
468
  const isPreloading = Object.values(router.matchCache).some(
472
469
  (d) =>
473
470
  d.match.isFetching &&
474
- !router.state.matches.find((dd) => dd.matchId === d.match.matchId),
471
+ !router.state.currentMatches.find(
472
+ (dd) => dd.matchId === d.match.matchId,
473
+ ),
475
474
  )
476
475
 
477
476
  if (
@@ -485,21 +484,20 @@ export function createRouter<
485
484
  }
486
485
  }
487
486
 
488
- cascadeLoaderData(router.state.matches)
487
+ cascadeLoaderData(router.state.currentMatches)
489
488
  router.listeners.forEach((listener) => listener(router))
490
489
  },
491
490
 
492
491
  dehydrate: () => {
493
492
  return {
494
- location: router.__location,
495
493
  state: {
496
494
  ...pick(router.state, [
495
+ 'latestLocation',
496
+ 'currentLocation',
497
497
  'status',
498
- 'location',
499
498
  'lastUpdated',
500
- 'location',
501
499
  ]),
502
- matches: router.state.matches.map((match) =>
500
+ currentMatches: router.state.currentMatches.map((match) =>
503
501
  pick(match, [
504
502
  'matchId',
505
503
  'status',
@@ -516,18 +514,22 @@ export function createRouter<
516
514
 
517
515
  hydrate: (dehydratedState) => {
518
516
  // Update the location
519
- router.__location = dehydratedState.location
517
+ router.state.latestLocation = dehydratedState.state.latestLocation
518
+ router.state.currentLocation = dehydratedState.state.currentLocation
520
519
 
521
520
  // Update the context
522
521
  router.options.context = dehydratedState.context
523
522
 
524
523
  // Match the routes
525
- const matches = router.matchRoutes(router.__location.pathname, {
526
- strictParseParams: true,
527
- })
524
+ const currentMatches = router.matchRoutes(
525
+ router.state.latestLocation.pathname,
526
+ {
527
+ strictParseParams: true,
528
+ },
529
+ )
528
530
 
529
- matches.forEach((match, index) => {
530
- const dehydratedMatch = dehydratedState.state.matches[index]
531
+ currentMatches.forEach((match, index) => {
532
+ const dehydratedMatch = dehydratedState.state.currentMatches[index]
531
533
  invariant(
532
534
  dehydratedMatch,
533
535
  'Oh no! Dehydrated route matches did not match the active state of the router 😬',
@@ -535,34 +537,24 @@ export function createRouter<
535
537
  Object.assign(match, dehydratedMatch)
536
538
  })
537
539
 
538
- matches.forEach((match) => match.__.validate())
540
+ currentMatches.forEach((match) => match.__.validate())
539
541
 
540
542
  router.state = {
541
543
  ...router.state,
542
544
  ...dehydratedState,
543
- matches,
545
+ currentMatches,
544
546
  }
545
547
  },
546
548
 
547
549
  mount: () => {
548
- const next = router.__.buildLocation({
549
- to: '.',
550
- search: true,
551
- hash: true,
552
- })
553
-
554
- // If the current location isn't updated, trigger a navigation
555
- // to the current location. Otherwise, load the current location.
556
- // if (next.href !== router.__location.href) {
557
- // router.__.commitLocation(next, true)
558
- // }
559
-
560
- if (!router.state.matches.length) {
550
+ if (!router.state.currentMatches.length) {
561
551
  router.load()
562
552
  }
563
553
 
564
554
  const unsub = router.history.listen((event) => {
565
- router.load(router.__.parseLocation(event.location, router.__location))
555
+ router.load(
556
+ router.__.parseLocation(event.location, router.state.latestLocation),
557
+ )
566
558
  })
567
559
 
568
560
  // addEventListener does not exist in React Native, but window does
@@ -589,12 +581,14 @@ export function createRouter<
589
581
 
590
582
  update: (opts) => {
591
583
  const newHistory = opts?.history !== router.history
592
- if (!router.__location || newHistory) {
584
+ if (!router.state.latestLocation || newHistory) {
593
585
  if (opts?.history) {
594
586
  router.history = opts.history
595
587
  }
596
- router.__location = router.__.parseLocation(router.history.location)
597
- router.state.location = router.__location
588
+ router.state.latestLocation = router.__.parseLocation(
589
+ router.history.location,
590
+ )
591
+ router.state.currentLocation = router.state.latestLocation
598
592
  }
599
593
 
600
594
  Object.assign(router.options, opts)
@@ -613,8 +607,8 @@ export function createRouter<
613
607
 
614
608
  cancelMatches: () => {
615
609
  ;[
616
- ...router.state.matches,
617
- ...(router.state.pending?.matches ?? []),
610
+ ...router.state.currentMatches,
611
+ ...(router.state.pendingMatches ?? []),
618
612
  ].forEach((match) => {
619
613
  match.cancel()
620
614
  })
@@ -626,63 +620,52 @@ export function createRouter<
626
620
 
627
621
  if (next) {
628
622
  // Ingest the new location
629
- router.__location = next
623
+ router.state.latestLocation = next
630
624
  }
631
625
 
632
626
  // Cancel any pending matches
633
627
  router.cancelMatches()
634
628
 
635
629
  // Match the routes
636
- const matches = router.matchRoutes(router.__location.pathname, {
630
+ const matches = router.matchRoutes(router.state.latestLocation.pathname, {
637
631
  strictParseParams: true,
638
632
  })
639
633
 
640
634
  if (typeof document !== 'undefined') {
641
635
  router.state = {
642
636
  ...router.state,
643
- pending: {
644
- matches: matches,
645
- location: router.__location,
646
- },
647
637
  status: 'loading',
638
+ pendingMatches: matches,
639
+ pendingLocation: router.state.latestLocation,
648
640
  }
649
641
  } else {
650
642
  router.state = {
651
643
  ...router.state,
652
- matches: matches,
653
- location: router.__location,
654
644
  status: 'loading',
645
+ currentMatches: matches,
646
+ currentLocation: router.state.latestLocation,
655
647
  }
656
648
  }
657
649
 
658
- // Check if each match middleware to see if the route can be accessed
659
- try {
660
- await Promise.all(
661
- matches.map((match) =>
662
- match.options.beforeLoad?.({
663
- router: router as any,
664
- match,
665
- }),
666
- ),
667
- )
668
- } catch (err: any) {
669
- if (err?.then) {
670
- await new Promise(() => {})
671
- }
672
- throw err
673
- }
674
-
675
650
  router.notify()
676
651
 
677
652
  // Load the matches
678
- await router.loadMatches(matches)
653
+ try {
654
+ await router.loadMatches(matches)
655
+ } catch (err: any) {
656
+ console.log(err)
657
+ invariant(
658
+ false,
659
+ 'Matches failed to load due to error above ☝️. Navigation cancelled!',
660
+ )
661
+ }
679
662
 
680
663
  if (router.startedLoadingAt !== id) {
681
664
  // Ignore side-effects of match loading
682
665
  return router.navigationPromise
683
666
  }
684
667
 
685
- const previousMatches = router.state.matches
668
+ const previousMatches = router.state.currentMatches
686
669
 
687
670
  const exiting: RouteMatch[] = [],
688
671
  staying: RouteMatch[] = []
@@ -712,10 +695,12 @@ export function createRouter<
712
695
  d.status = 'idle'
713
696
  d.error = undefined
714
697
  }
698
+
715
699
  const gc = Math.max(
716
700
  d.options.loaderGcMaxAge ?? router.options.defaultLoaderGcMaxAge ?? 0,
717
701
  d.options.loaderMaxAge ?? router.options.defaultLoaderMaxAge ?? 0,
718
702
  )
703
+
719
704
  if (gc > 0) {
720
705
  router.matchCache[d.matchId] = {
721
706
  gc: gc == Infinity ? Number.MAX_SAFE_INTEGER : now + gc,
@@ -754,10 +739,11 @@ export function createRouter<
754
739
 
755
740
  router.state = {
756
741
  ...router.state,
757
- location: router.__location,
758
- matches,
759
- pending: undefined,
760
742
  status: 'idle',
743
+ currentLocation: router.state.latestLocation,
744
+ currentMatches: matches,
745
+ pendingLocation: undefined,
746
+ pendingMatches: undefined,
761
747
  }
762
748
 
763
749
  router.notify()
@@ -785,7 +771,7 @@ export function createRouter<
785
771
  })
786
772
  },
787
773
 
788
- loadRoute: async (navigateOpts = router.__location) => {
774
+ loadRoute: async (navigateOpts = router.state.latestLocation) => {
789
775
  const next = router.buildNext(navigateOpts)
790
776
  const matches = router.matchRoutes(next.pathname, {
791
777
  strictParseParams: true,
@@ -794,11 +780,15 @@ export function createRouter<
794
780
  return matches
795
781
  },
796
782
 
797
- preloadRoute: async (navigateOpts = router.__location, loaderOpts) => {
783
+ preloadRoute: async (
784
+ navigateOpts = router.state.latestLocation,
785
+ loaderOpts,
786
+ ) => {
798
787
  const next = router.buildNext(navigateOpts)
799
788
  const matches = router.matchRoutes(next.pathname, {
800
789
  strictParseParams: true,
801
790
  })
791
+
802
792
  await router.loadMatches(matches, {
803
793
  preload: true,
804
794
  maxAge:
@@ -825,8 +815,8 @@ export function createRouter<
825
815
  }
826
816
 
827
817
  const existingMatches = [
828
- ...router.state.matches,
829
- ...(router.state.pending?.matches ?? []),
818
+ ...router.state.currentMatches,
819
+ ...(router.state.pendingMatches ?? []),
830
820
  ]
831
821
 
832
822
  const recurse = async (routes: Route<any, any>[]): Promise<void> => {
@@ -857,6 +847,14 @@ export function createRouter<
857
847
  route.options.caseSensitive ?? router.options.caseSensitive,
858
848
  })
859
849
 
850
+ // console.log(
851
+ // router.basepath,
852
+ // route.fullPath,
853
+ // fuzzy,
854
+ // pathname,
855
+ // matchParams,
856
+ // )
857
+
860
858
  if (matchParams) {
861
859
  let parsedParams
862
860
 
@@ -923,10 +921,30 @@ export function createRouter<
923
921
  },
924
922
 
925
923
  loadMatches: async (resolvedMatches, loaderOpts) => {
926
- const matchPromises = resolvedMatches.map(async (match) => {
924
+ resolvedMatches.forEach(async (match) => {
927
925
  // Validate the match (loads search params etc)
928
926
  match.__.validate()
927
+ })
928
+
929
+ // Check each match middleware to see if the route can be accessed
930
+ await Promise.all(
931
+ resolvedMatches.map(async (match) => {
932
+ try {
933
+ await match.options.beforeLoad?.({
934
+ router: router as any,
935
+ match,
936
+ })
937
+ } catch (err) {
938
+ if (!loaderOpts?.preload) {
939
+ match.options.onLoadError?.(err)
940
+ }
941
+
942
+ throw err
943
+ }
944
+ }),
945
+ )
929
946
 
947
+ const matchPromises = resolvedMatches.map(async (match) => {
930
948
  const search = match.search as { __data?: any }
931
949
 
932
950
  if (search.__data?.matchId && search.__data.matchId !== match.matchId) {
@@ -996,8 +1014,8 @@ export function createRouter<
996
1014
  .matchRoutes(next.pathname)
997
1015
  .map((d) => d.matchId)
998
1016
  ;[
999
- ...router.state.matches,
1000
- ...(router.state.pending?.matches ?? []),
1017
+ ...router.state.currentMatches,
1018
+ ...(router.state.pendingMatches ?? []),
1001
1019
  ].forEach((match) => {
1002
1020
  if (unloadedMatchIds.includes(match.matchId)) {
1003
1021
  match.invalidate()
@@ -1029,12 +1047,13 @@ export function createRouter<
1029
1047
  const next = router.buildNext(location)
1030
1048
 
1031
1049
  if (opts?.pending) {
1032
- if (!router.state.pending?.location) {
1050
+ if (!router.state.pendingLocation) {
1033
1051
  return false
1034
1052
  }
1053
+
1035
1054
  return !!matchPathname(
1036
1055
  router.basepath,
1037
- router.state.pending.location.pathname,
1056
+ router.state.pendingLocation.pathname,
1038
1057
  {
1039
1058
  ...opts,
1040
1059
  to: next.pathname,
@@ -1042,10 +1061,14 @@ export function createRouter<
1042
1061
  )
1043
1062
  }
1044
1063
 
1045
- return !!matchPathname(router.basepath, router.state.location.pathname, {
1046
- ...opts,
1047
- to: next.pathname,
1048
- })
1064
+ return !!matchPathname(
1065
+ router.basepath,
1066
+ router.state.currentLocation.pathname,
1067
+ {
1068
+ ...opts,
1069
+ to: next.pathname,
1070
+ },
1071
+ )
1049
1072
  },
1050
1073
 
1051
1074
  navigate: async ({ from, to = '.', search, hash, replace, params }) => {
@@ -1124,13 +1147,14 @@ export function createRouter<
1124
1147
  userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0
1125
1148
 
1126
1149
  // Compare path/hash for matches
1127
- const pathIsEqual = router.state.location.pathname === next.pathname
1128
- const currentPathSplit = router.state.location.pathname.split('/')
1150
+ const pathIsEqual =
1151
+ router.state.currentLocation.pathname === next.pathname
1152
+ const currentPathSplit = router.state.currentLocation.pathname.split('/')
1129
1153
  const nextPathSplit = next.pathname.split('/')
1130
1154
  const pathIsFuzzyEqual = nextPathSplit.every(
1131
1155
  (d, i) => d === currentPathSplit[i],
1132
1156
  )
1133
- const hashIsEqual = router.state.location.hash === next.hash
1157
+ const hashIsEqual = router.state.currentLocation.hash === next.hash
1134
1158
  // Combine the matches based on user options
1135
1159
  const pathTest = activeOptions?.exact ? pathIsEqual : pathIsFuzzyEqual
1136
1160
  const hashTest = activeOptions?.includeHash ? hashIsEqual : true
@@ -1160,10 +1184,15 @@ export function createRouter<
1160
1184
  // The click handler
1161
1185
  const handleFocus = (e: MouseEvent) => {
1162
1186
  if (preload) {
1163
- router.preloadRoute(nextOpts, {
1164
- maxAge: userPreloadMaxAge,
1165
- gcMaxAge: userPreloadGcMaxAge,
1166
- })
1187
+ router
1188
+ .preloadRoute(nextOpts, {
1189
+ maxAge: userPreloadMaxAge,
1190
+ gcMaxAge: userPreloadGcMaxAge,
1191
+ })
1192
+ .catch((err) => {
1193
+ console.log(err)
1194
+ console.warn('Error preloading route! ☝️')
1195
+ })
1167
1196
  }
1168
1197
  }
1169
1198
 
@@ -1177,10 +1206,15 @@ export function createRouter<
1177
1206
 
1178
1207
  target.preloadTimeout = setTimeout(() => {
1179
1208
  target.preloadTimeout = null
1180
- router.preloadRoute(nextOpts, {
1181
- maxAge: userPreloadMaxAge,
1182
- gcMaxAge: userPreloadGcMaxAge,
1183
- })
1209
+ router
1210
+ .preloadRoute(nextOpts, {
1211
+ maxAge: userPreloadMaxAge,
1212
+ gcMaxAge: userPreloadGcMaxAge,
1213
+ })
1214
+ .catch((err) => {
1215
+ console.log(err)
1216
+ console.warn('Error preloading route! ☝️')
1217
+ })
1184
1218
  }, preloadDelay)
1185
1219
  }
1186
1220
  }
@@ -1290,8 +1324,8 @@ export function createRouter<
1290
1324
 
1291
1325
  buildLocation: (dest: BuildNextOptions = {}): Location => {
1292
1326
  const fromPathname = dest.fromCurrent
1293
- ? router.__location.pathname
1294
- : dest.from ?? router.__location.pathname
1327
+ ? router.state.latestLocation.pathname
1328
+ : dest.from ?? router.state.latestLocation.pathname
1295
1329
 
1296
1330
  let pathname = resolvePath(
1297
1331
  router.basepath ?? '/',
@@ -1299,9 +1333,12 @@ export function createRouter<
1299
1333
  `${dest.to ?? '.'}`,
1300
1334
  )
1301
1335
 
1302
- const fromMatches = router.matchRoutes(router.__location.pathname, {
1303
- strictParseParams: true,
1304
- })
1336
+ const fromMatches = router.matchRoutes(
1337
+ router.state.latestLocation.pathname,
1338
+ {
1339
+ strictParseParams: true,
1340
+ },
1341
+ )
1305
1342
 
1306
1343
  const toMatches = router.matchRoutes(pathname)
1307
1344
 
@@ -1327,9 +1364,9 @@ export function createRouter<
1327
1364
  const preFilteredSearch = dest.__preSearchFilters?.length
1328
1365
  ? dest.__preSearchFilters.reduce(
1329
1366
  (prev, next) => next(prev),
1330
- router.__location.search,
1367
+ router.state.latestLocation.search,
1331
1368
  )
1332
- : router.__location.search
1369
+ : router.state.latestLocation.search
1333
1370
 
1334
1371
  // Then the link/navigate function
1335
1372
  const destSearch =
@@ -1350,22 +1387,22 @@ export function createRouter<
1350
1387
  : destSearch
1351
1388
 
1352
1389
  const search = replaceEqualDeep(
1353
- router.__location.search,
1390
+ router.state.latestLocation.search,
1354
1391
  postFilteredSearch,
1355
1392
  )
1356
1393
 
1357
1394
  const searchStr = router.options.stringifySearch(search)
1358
1395
  let hash =
1359
1396
  dest.hash === true
1360
- ? router.__location.hash
1361
- : functionalUpdate(dest.hash!, router.__location.hash)
1397
+ ? router.state.latestLocation.hash
1398
+ : functionalUpdate(dest.hash!, router.state.latestLocation.hash)
1362
1399
  hash = hash ? `#${hash}` : ''
1363
1400
 
1364
1401
  return {
1365
1402
  pathname,
1366
1403
  search,
1367
1404
  searchStr,
1368
- state: router.__location.state,
1405
+ state: router.state.latestLocation.state,
1369
1406
  hash,
1370
1407
  href: `${pathname}${searchStr}${hash}`,
1371
1408
  key: dest.key,