@tanstack/react-router 0.0.1-beta.215 → 0.0.1-beta.217

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.215",
4
+ "version": "0.0.1-beta.217",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
7
  "homepage": "https://tanstack.com/router",
@@ -42,7 +42,7 @@
42
42
  "@babel/runtime": "^7.16.7",
43
43
  "tiny-invariant": "^1.3.1",
44
44
  "tiny-warning": "^1.0.3",
45
- "@tanstack/history": "0.0.1-beta.215"
45
+ "@tanstack/history": "0.0.1-beta.217"
46
46
  },
47
47
  "scripts": {
48
48
  "build": "rollup --config rollup.config.js"
@@ -27,7 +27,13 @@ import {
27
27
  trimPathRight,
28
28
  } from './path'
29
29
  import { isRedirect } from './redirects'
30
- import { AnyPathParams, AnyRoute, AnySearchSchema, Route } from './route'
30
+ import {
31
+ AnyPathParams,
32
+ AnyRoute,
33
+ AnySearchSchema,
34
+ LoaderFnContext,
35
+ Route,
36
+ } from './route'
31
37
  import {
32
38
  FullSearchSchema,
33
39
  ParseRoute,
@@ -51,7 +57,7 @@ import {
51
57
  PickAsRequired,
52
58
  functionalUpdate,
53
59
  last,
54
- partialDeepEqual,
60
+ deepEqual,
55
61
  pick,
56
62
  replaceEqualDeep,
57
63
  useStableCallback,
@@ -486,6 +492,7 @@ export function RouterProvider<
486
492
  loadPromise: Promise.resolve(),
487
493
  context: undefined!,
488
494
  abortController: new AbortController(),
495
+ shouldReloadDeps: undefined,
489
496
  fetchedAt: 0,
490
497
  }
491
498
 
@@ -960,22 +967,11 @@ export function RouterProvider<
960
967
  if (match.isFetching) {
961
968
  loadPromise = getRouteMatch(state, match.id)?.loadPromise
962
969
  } else {
963
- matches[index] = match = {
964
- ...match,
965
- isFetching: true,
966
- }
970
+ const cause = state.matches.find((d) => d.id === match.id)
971
+ ? 'stay'
972
+ : 'enter'
967
973
 
968
- const componentsPromise = Promise.all(
969
- componentTypes.map(async (type) => {
970
- const component = route.options[type]
971
-
972
- if ((component as any)?.preload) {
973
- await (component as any).preload()
974
- }
975
- }),
976
- )
977
-
978
- const loaderPromise = route.options.loader?.({
974
+ const loaderContext: LoaderFnContext = {
979
975
  params: match.params,
980
976
  search: match.search,
981
977
  preload: !!preload,
@@ -985,12 +981,62 @@ export function RouterProvider<
985
981
  location: state.location,
986
982
  navigate: (opts) =>
987
983
  navigate({ ...opts, from: match.pathname } as any),
988
- })
984
+ cause,
985
+ }
986
+
987
+ // Default to reloading the route all the time
988
+ let shouldReload = true
989
+ let shouldReloadDeps =
990
+ typeof route.options.shouldReload === 'function'
991
+ ? route.options.shouldReload?.(loaderContext)
992
+ : !!route.options.shouldReload
993
+
994
+ if (typeof shouldReloadDeps === 'object') {
995
+ // compare the deps to see if they've changed
996
+ shouldReload = !deepEqual(
997
+ shouldReloadDeps,
998
+ match.shouldReloadDeps,
999
+ )
1000
+ console.log(
1001
+ shouldReloadDeps,
1002
+ match.shouldReloadDeps,
1003
+ shouldReload,
1004
+ )
1005
+
1006
+ match.shouldReloadDeps = shouldReloadDeps
1007
+ } else {
1008
+ shouldReload = !!shouldReloadDeps
1009
+ }
1010
+
1011
+ // If the user doesn't want the route to reload, just
1012
+ // resolve with the existing loader data
1013
+
1014
+ if (!shouldReload) {
1015
+ loadPromise = Promise.resolve(match.loaderData)
1016
+ } else {
1017
+ // Otherwise, load the route
1018
+ matches[index] = match = {
1019
+ ...match,
1020
+ isFetching: true,
1021
+ }
989
1022
 
990
- loadPromise = Promise.all([
991
- componentsPromise,
992
- loaderPromise,
993
- ]).then((d) => d[1])
1023
+ const componentsPromise = Promise.all(
1024
+ componentTypes.map(async (type) => {
1025
+ const component = route.options[type]
1026
+
1027
+ if ((component as any)?.preload) {
1028
+ await (component as any).preload()
1029
+ }
1030
+ }),
1031
+ )
1032
+
1033
+ const loaderPromise = route.options.loader?.(loaderContext)
1034
+
1035
+ loadPromise = Promise.all([
1036
+ componentsPromise,
1037
+ loaderPromise,
1038
+ ]).then((d) => d[1])
1039
+ }
994
1040
  }
995
1041
 
996
1042
  matches[index] = match = {
@@ -1226,7 +1272,7 @@ export function RouterProvider<
1226
1272
  : true
1227
1273
  const searchTest =
1228
1274
  activeOptions?.includeSearch ?? true
1229
- ? partialDeepEqual(latestLocationRef.current.search, next.search)
1275
+ ? deepEqual(latestLocationRef.current.search, next.search, true)
1230
1276
  : true
1231
1277
 
1232
1278
  // The final "active" test
@@ -1381,9 +1427,7 @@ export function RouterProvider<
1381
1427
  }
1382
1428
 
1383
1429
  if (match && (opts?.includeSearch ?? true)) {
1384
- return partialDeepEqual(baseLocation.search, next.search)
1385
- ? match
1386
- : false
1430
+ return deepEqual(baseLocation.search, next.search, true) ? match : false
1387
1431
  }
1388
1432
 
1389
1433
  return match
@@ -1468,6 +1512,7 @@ export interface RouteMatch<
1468
1512
  search: FullSearchSchema<TRouteTree> &
1469
1513
  RouteById<TRouteTree, TRouteId>['types']['fullSearchSchema']
1470
1514
  fetchedAt: number
1515
+ shouldReloadDeps: any
1471
1516
  abortController: AbortController
1472
1517
  }
1473
1518
 
package/src/route.ts CHANGED
@@ -108,6 +108,16 @@ export type BaseRouteOptions<
108
108
  > = RoutePathOptions<TCustomId, TPath> & {
109
109
  getParentRoute: () => TParentRoute
110
110
  validateSearch?: SearchSchemaValidator<TSearchSchema>
111
+ shouldReload?:
112
+ | boolean
113
+ | ((
114
+ match: LoaderFnContext<
115
+ TAllParams,
116
+ TFullSearchSchema,
117
+ TAllContext,
118
+ TRouteContext
119
+ >,
120
+ ) => any)
111
121
  } & (keyof PickRequired<RouteContext> extends never
112
122
  ? // This async function is called before a route is loaded.
113
123
  // If an error is thrown here, the route's loader will not be called.
@@ -268,9 +278,7 @@ export type RouteLoadFn<
268
278
  TFullSearchSchema,
269
279
  TAllContext,
270
280
  TRouteContext
271
- > & {
272
- parentMatchPromise?: Promise<void>
273
- },
281
+ >,
274
282
  ) => Promise<TLoaderData> | TLoaderData
275
283
 
276
284
  export interface LoaderFnContext<
@@ -286,6 +294,8 @@ export interface LoaderFnContext<
286
294
  context: Expand<Assign<TAllContext, TRouteContext>>
287
295
  location: ParsedLocation<TFullSearchSchema>
288
296
  navigate: (opts: NavigateOptions<AnyRoute>) => Promise<void>
297
+ parentMatchPromise?: Promise<void>
298
+ cause: 'enter' | 'stay'
289
299
  }
290
300
 
291
301
  export type SearchFilter<T, U = T> = (prev: T) => U
package/src/utils.ts CHANGED
@@ -231,7 +231,7 @@ function hasObjectPrototype(o: any) {
231
231
  return Object.prototype.toString.call(o) === '[object Object]'
232
232
  }
233
233
 
234
- export function partialDeepEqual(a: any, b: any): boolean {
234
+ export function deepEqual(a: any, b: any, partial: boolean = false): boolean {
235
235
  if (a === b) {
236
236
  return true
237
237
  }
@@ -241,14 +241,20 @@ export function partialDeepEqual(a: any, b: any): boolean {
241
241
  }
242
242
 
243
243
  if (isPlainObject(a) && isPlainObject(b)) {
244
- return !Object.keys(b).some((key) => !partialDeepEqual(a[key], b[key]))
244
+ const aKeys = Object.keys(a)
245
+ const bKeys = Object.keys(b)
246
+
247
+ if (!partial && aKeys.length !== bKeys.length) {
248
+ return false
249
+ }
250
+
251
+ return !bKeys.some(
252
+ (key) => !(key in a) || !deepEqual(a[key], b[key], partial),
253
+ )
245
254
  }
246
255
 
247
256
  if (Array.isArray(a) && Array.isArray(b)) {
248
- return !(
249
- a.length !== b.length ||
250
- a.some((item, index) => !partialDeepEqual(item, b[index]))
251
- )
257
+ return !a.some((item, index) => !deepEqual(item, b[index], partial))
252
258
  }
253
259
 
254
260
  return false