@tanstack/react-router 0.0.1-beta.205 → 0.0.1-beta.207

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/react-router",
3
3
  "author": "Tanner Linsley",
4
- "version": "0.0.1-beta.205",
4
+ "version": "0.0.1-beta.207",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
7
  "homepage": "https://tanstack.com/router",
@@ -40,11 +40,9 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@babel/runtime": "^7.16.7",
43
- "@gisatcz/cross-package-react-context": "^0.2.0",
44
- "@tanstack/react-router": "^0.0.1-beta.203",
45
43
  "tiny-invariant": "^1.3.1",
46
44
  "tiny-warning": "^1.0.3",
47
- "@tanstack/history": "0.0.1-beta.205"
45
+ "@tanstack/history": "0.0.1-beta.207"
48
46
  },
49
47
  "scripts": {
50
48
  "build": "rollup --config rollup.config.js"
@@ -134,6 +134,10 @@ export type RouterContext<
134
134
 
135
135
  export const routerContext = React.createContext<RouterContext<any>>(null!)
136
136
 
137
+ if (typeof document !== 'undefined') {
138
+ window.__TSR_ROUTER_CONTEXT__ = routerContext as any
139
+ }
140
+
137
141
  export function getInitialRouterState(
138
142
  location: ParsedLocation,
139
143
  ): RouterState<any> {
@@ -175,6 +179,14 @@ export function RouterProvider<
175
179
 
176
180
  const navigateTimeoutRef = React.useRef<NodeJS.Timeout | null>(null)
177
181
 
182
+ const latestLoadPromiseRef = React.useRef<Promise<void>>(Promise.resolve())
183
+
184
+ const checkLatest = (promise: Promise<void>): undefined | Promise<void> => {
185
+ return latestLoadPromiseRef.current !== promise
186
+ ? latestLoadPromiseRef.current
187
+ : undefined
188
+ }
189
+
178
190
  const parseLocation = useStableCallback(
179
191
  (
180
192
  previousLocation?: ParsedLocation,
@@ -346,8 +358,6 @@ export function RouterProvider<
346
358
  [routesByPath],
347
359
  )
348
360
 
349
- const latestLoadPromiseRef = React.useRef<Promise<void>>(Promise.resolve())
350
-
351
361
  const matchRoutes = useStableCallback(
352
362
  <TRouteTree extends AnyRoute>(
353
363
  pathname: string,
@@ -528,7 +538,7 @@ export function RouterProvider<
528
538
  } = {},
529
539
  matches?: AnyRouteMatch[],
530
540
  ): ParsedLocation => {
531
- const from = state.location
541
+ const from = latestLocationRef.current
532
542
  const fromPathname = dest.from ?? from.pathname
533
543
 
534
544
  let pathname = resolvePathWithBase(fromPathname, `${dest.to ?? ''}`)
@@ -697,7 +707,7 @@ export function RouterProvider<
697
707
  async (next: ParsedLocation & CommitLocationOptions) => {
698
708
  if (navigateTimeoutRef.current) clearTimeout(navigateTimeoutRef.current)
699
709
 
700
- const isSameUrl = state.location.href === next.href
710
+ const isSameUrl = latestLocationRef.current.href === next.href
701
711
 
702
712
  // If the next urls are the same and we're not replacing,
703
713
  // do nothing
@@ -741,7 +751,7 @@ export function RouterProvider<
741
751
  )
742
752
 
743
753
  const buildAndCommitLocation = useStableCallback(
744
- <TRouteTree extends AnyRoute>({
754
+ ({
745
755
  replace,
746
756
  resetScroll,
747
757
  ...rest
@@ -786,12 +796,15 @@ export function RouterProvider<
786
796
 
787
797
  const loadMatches = useStableCallback(
788
798
  async ({
799
+ checkLatest,
789
800
  matches,
790
801
  preload,
791
802
  }: {
803
+ checkLatest: () => Promise<void> | undefined
792
804
  matches: AnyRouteMatch[]
793
805
  preload?: boolean
794
- }) => {
806
+ }): Promise<RouteMatch[]> => {
807
+ let latestPromise
795
808
  let firstBadMatchIndex: number | undefined
796
809
 
797
810
  // Check each match middleware to see if the route can be accessed
@@ -844,7 +857,7 @@ export function RouterProvider<
844
857
  params: match.params,
845
858
  preload: !!preload,
846
859
  meta: parentMeta,
847
- location: state.location,
860
+ location: state.location, // TODO: This might need to be latestLocationRef.current...?
848
861
  })) ?? ({} as any)
849
862
 
850
863
  const meta = {
@@ -864,7 +877,7 @@ export function RouterProvider<
864
877
  } catch (err) {
865
878
  if (isRedirect(err)) {
866
879
  if (!preload) navigate(err as any)
867
- return
880
+ return matches
868
881
  }
869
882
 
870
883
  throw err
@@ -883,14 +896,6 @@ export function RouterProvider<
883
896
  return getRouteMatch(state, match.id)?.loadPromise
884
897
  }
885
898
 
886
- const fetchedAt = Date.now()
887
- const checkLatest = () => {
888
- const latest = getRouteMatch(state, match.id)
889
- return latest && latest.fetchedAt !== fetchedAt
890
- ? latest.loadPromise
891
- : undefined
892
- }
893
-
894
899
  const handleIfRedirect = (err: any) => {
895
900
  if (isRedirect(err)) {
896
901
  if (!preload) {
@@ -902,8 +907,6 @@ export function RouterProvider<
902
907
  }
903
908
 
904
909
  const load = async () => {
905
- let latestPromise
906
-
907
910
  try {
908
911
  const componentsPromise = Promise.all(
909
912
  componentTypes.map(async (type) => {
@@ -953,6 +956,15 @@ export function RouterProvider<
953
956
  updatedAt: Date.now(),
954
957
  }
955
958
  }
959
+
960
+ if (!preload) {
961
+ setState((s) => ({
962
+ ...s,
963
+ matches: s.matches.map((d) =>
964
+ d.id === match.id ? match : d,
965
+ ),
966
+ }))
967
+ }
956
968
  }
957
969
 
958
970
  let loadPromise: Promise<void> | undefined
@@ -960,7 +972,7 @@ export function RouterProvider<
960
972
  matches[index] = match = {
961
973
  ...match,
962
974
  isFetching: true,
963
- fetchedAt,
975
+ fetchedAt: Date.now(),
964
976
  invalid: false,
965
977
  }
966
978
 
@@ -977,48 +989,38 @@ export function RouterProvider<
977
989
  })
978
990
 
979
991
  await Promise.all(matchPromises)
992
+ return matches
980
993
  },
981
994
  )
982
995
 
983
- const load = useStableCallback<LoadFn>(async (opts) => {
996
+ const load = useStableCallback<LoadFn>(async () => {
984
997
  const promise = new Promise<void>(async (resolve, reject) => {
998
+ const next = latestLocationRef.current
985
999
  const prevLocation = state.resolvedLocation
986
- const pathDidChange = !!(
987
- opts?.next && prevLocation!.href !== opts.next.href
988
- )
989
-
1000
+ const pathDidChange = !!(next && prevLocation!.href !== next.href)
990
1001
  let latestPromise: Promise<void> | undefined | null
991
1002
 
992
- const checkLatest = (): undefined | Promise<void> | null => {
993
- return latestLoadPromiseRef.current !== promise
994
- ? latestLoadPromiseRef.current
995
- : undefined
996
- }
997
-
998
1003
  // Cancel any pending matches
999
1004
  cancelMatches(state)
1000
1005
 
1001
1006
  router.emit({
1002
1007
  type: 'onBeforeLoad',
1003
1008
  from: prevLocation,
1004
- to: opts?.next ?? state.location,
1009
+ to: next ?? state.location,
1005
1010
  pathChanged: pathDidChange,
1006
1011
  })
1007
1012
 
1008
- if (opts?.next) {
1009
- // Ingest the new location
1010
- setState((s) => ({
1011
- ...s,
1012
- location: opts.next! as any,
1013
- }))
1014
- }
1013
+ // Ingest the new location
1014
+ setState((s) => ({
1015
+ ...s,
1016
+ location: next,
1017
+ }))
1015
1018
 
1016
1019
  // Match the routes
1017
- const matches: RouteMatch<any, any>[] = matchRoutes(
1018
- state.location.pathname,
1019
- state.location.search,
1020
+ let matches: RouteMatch<any, any>[] = matchRoutes(
1021
+ next.pathname,
1022
+ next.search,
1020
1023
  {
1021
- throwOnError: opts?.throwOnError,
1022
1024
  debug: true,
1023
1025
  },
1024
1026
  )
@@ -1030,10 +1032,11 @@ export function RouterProvider<
1030
1032
  }))
1031
1033
 
1032
1034
  try {
1033
- // Load the matches
1034
1035
  try {
1036
+ // Load the matches
1035
1037
  await loadMatches({
1036
1038
  matches,
1039
+ checkLatest: () => checkLatest(promise),
1037
1040
  })
1038
1041
  } catch (err) {
1039
1042
  // swallow this error, since we'll display the
@@ -1041,7 +1044,7 @@ export function RouterProvider<
1041
1044
  }
1042
1045
 
1043
1046
  // Only apply the latest transition
1044
- if ((latestPromise = checkLatest())) {
1047
+ if ((latestPromise = checkLatest(promise))) {
1045
1048
  return latestPromise
1046
1049
  }
1047
1050
 
@@ -1078,14 +1081,14 @@ export function RouterProvider<
1078
1081
  router.emit({
1079
1082
  type: 'onLoad',
1080
1083
  from: prevLocation,
1081
- to: state.location,
1084
+ to: next,
1082
1085
  pathChanged: pathDidChange,
1083
1086
  })
1084
1087
 
1085
1088
  resolve()
1086
1089
  } catch (err) {
1087
1090
  // Only apply the latest transition
1088
- if ((latestPromise = checkLatest())) {
1091
+ if ((latestPromise = checkLatest(promise))) {
1089
1092
  return latestPromise
1090
1093
  }
1091
1094
 
@@ -1098,14 +1101,6 @@ export function RouterProvider<
1098
1101
  return latestLoadPromiseRef.current
1099
1102
  })
1100
1103
 
1101
- const safeLoad = React.useCallback(async () => {
1102
- try {
1103
- return load()
1104
- } catch (err) {
1105
- // Don't do anything
1106
- }
1107
- }, [])
1108
-
1109
1104
  const preloadRoute = useStableCallback(
1110
1105
  async (navigateOpts: BuildNextOptions = state.location) => {
1111
1106
  let next = buildLocation(navigateOpts)
@@ -1117,6 +1112,7 @@ export function RouterProvider<
1117
1112
  await loadMatches({
1118
1113
  matches,
1119
1114
  preload: true,
1115
+ checkLatest: () => undefined,
1120
1116
  })
1121
1117
 
1122
1118
  return [last(matches)!, matches] as const
@@ -1158,21 +1154,21 @@ export function RouterProvider<
1158
1154
  const preloadDelay = userPreloadDelay ?? options.defaultPreloadDelay ?? 0
1159
1155
 
1160
1156
  // Compare path/hash for matches
1161
- const currentPathSplit = state.location.pathname.split('/')
1157
+ const currentPathSplit = latestLocationRef.current.pathname.split('/')
1162
1158
  const nextPathSplit = next.pathname.split('/')
1163
1159
  const pathIsFuzzyEqual = nextPathSplit.every(
1164
1160
  (d, i) => d === currentPathSplit[i],
1165
1161
  )
1166
1162
  // Combine the matches based on user options
1167
1163
  const pathTest = activeOptions?.exact
1168
- ? state.location.pathname === next.pathname
1164
+ ? latestLocationRef.current.pathname === next.pathname
1169
1165
  : pathIsFuzzyEqual
1170
1166
  const hashTest = activeOptions?.includeHash
1171
- ? state.location.hash === next.hash
1167
+ ? latestLocationRef.current.hash === next.hash
1172
1168
  : true
1173
1169
  const searchTest =
1174
1170
  activeOptions?.includeSearch ?? true
1175
- ? partialDeepEqual(state.location.search, next.search)
1171
+ ? partialDeepEqual(latestLocationRef.current.search, next.search)
1176
1172
  : true
1177
1173
 
1178
1174
  // The final "active" test
@@ -1252,13 +1248,20 @@ export function RouterProvider<
1252
1248
  },
1253
1249
  )
1254
1250
 
1251
+ const latestLocationRef = React.useRef(state.location)
1252
+
1255
1253
  React.useLayoutEffect(() => {
1256
1254
  const unsub = history.subscribe(() => {
1255
+ latestLocationRef.current = parseLocation(latestLocationRef.current)
1256
+
1257
1257
  React.startTransition(() => {
1258
- setState((s) => ({
1259
- ...s,
1260
- location: parseLocation(state.location),
1261
- }))
1258
+ if (state.location !== latestLocationRef.current) {
1259
+ try {
1260
+ load()
1261
+ } catch (err) {
1262
+ console.error(err)
1263
+ }
1264
+ }
1262
1265
  })
1263
1266
  })
1264
1267
 
@@ -1282,14 +1285,12 @@ export function RouterProvider<
1282
1285
 
1283
1286
  if (initialLoad.current) {
1284
1287
  initialLoad.current = false
1285
- safeLoad()
1286
- }
1287
-
1288
- React.useLayoutEffect(() => {
1289
- if (state.resolvedLocation !== state.location) {
1290
- safeLoad()
1288
+ try {
1289
+ load()
1290
+ } catch (err) {
1291
+ console.error(err)
1291
1292
  }
1292
- }, [state.location])
1293
+ }
1293
1294
 
1294
1295
  const isFetching = React.useMemo(
1295
1296
  () => [...state.matches, ...state.pendingMatches].some((d) => d.isFetching),
@@ -1309,7 +1310,9 @@ export function RouterProvider<
1309
1310
  return false
1310
1311
  }
1311
1312
 
1312
- const baseLocation = opts?.pending ? state.location : state.resolvedLocation
1313
+ const baseLocation = opts?.pending
1314
+ ? latestLocationRef.current
1315
+ : state.resolvedLocation
1313
1316
 
1314
1317
  if (!baseLocation) {
1315
1318
  return false
package/src/react.tsx CHANGED
@@ -404,7 +404,8 @@ export type RouterProps<
404
404
  export function useRouter<
405
405
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
406
406
  >(): RouterContext<TRouteTree> {
407
- const value = React.useContext(routerContext)
407
+ const resolvedContext = window.__TSR_ROUTER_CONTEXT__ || routerContext
408
+ const value = React.useContext(resolvedContext)
408
409
  warning(value, 'useRouter must be used inside a <RouterProvider> component!')
409
410
  return value as any
410
411
  }
package/src/router.ts CHANGED
@@ -21,12 +21,14 @@ import { RouteMatch } from './RouteMatch'
21
21
  import { ParsedLocation } from './location'
22
22
  import { LocationState } from './location'
23
23
  import { SearchSerializer, SearchParser } from './searchParams'
24
+ import { RouterContext } from './RouterProvider'
24
25
 
25
26
  //
26
27
 
27
28
  declare global {
28
29
  interface Window {
29
30
  __TSR_DEHYDRATED__?: HydrationCtx
31
+ __TSR_ROUTER_CONTEXT__?: React.Context<RouterContext<any>>
30
32
  }
31
33
  }
32
34