@tanstack/react-router 0.0.1-beta.225 → 0.0.1-beta.227

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.
@@ -20,7 +20,6 @@ export interface RouteMatch<TRouteTree extends AnyRoute = AnyRoute, TRouteId ext
20
20
  loaderData?: RouteById<TRouteTree, TRouteId>['types']['loaderData'];
21
21
  __resolveLoadPromise?: () => void;
22
22
  context: RouteById<TRouteTree, TRouteId>['types']['allContext'];
23
- routeSearch: RouteById<TRouteTree, TRouteId>['types']['searchSchema'];
24
23
  search: FullSearchSchema<TRouteTree> & RouteById<TRouteTree, TRouteId>['types']['fullSearchSchema'];
25
24
  fetchedAt: number;
26
25
  shouldReloadDeps: any;
@@ -3,7 +3,7 @@ import { LinkInfo, LinkOptions, NavigateOptions, ResolveRelativePath, ToOptions
3
3
  import { ParsedLocation } from './location';
4
4
  import { AnyRoute } from './route';
5
5
  import { RouteById, RoutePaths } from './routeInfo';
6
- import { BuildNextOptions, DehydratedRouteMatch, RegisteredRouter, Router, RouterOptions, RouterState } from './router';
6
+ import { BuildNextOptions, RegisteredRouter, Router, RouterOptions, RouterState } from './router';
7
7
  import { NoInfer } from './utils';
8
8
  import { MatchRouteOptions } from './Matches';
9
9
  import { RouteMatch } from './Matches';
@@ -21,11 +21,6 @@ export interface MatchLocation {
21
21
  export type BuildLinkFn<TRouteTree extends AnyRoute> = <TFrom extends RoutePaths<TRouteTree> = '/', TTo extends string = ''>(dest: LinkOptions<TRouteTree, TFrom, TTo>) => LinkInfo;
22
22
  export type NavigateFn<TRouteTree extends AnyRoute> = <TFrom extends RoutePaths<TRouteTree> = '/', TTo extends string = '', TMaskFrom extends RoutePaths<TRouteTree> = TFrom, TMaskTo extends string = ''>(opts: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>) => Promise<void>;
23
23
  export type MatchRouteFn<TRouteTree extends AnyRoute> = <TFrom extends RoutePaths<TRouteTree> = '/', TTo extends string = '', TResolved = ResolveRelativePath<TFrom, NoInfer<TTo>>>(location: ToOptions<TRouteTree, TFrom, TTo>, opts?: MatchRouteOptions) => false | RouteById<TRouteTree, TResolved>['types']['allParams'];
24
- export type LoadFn = (opts?: {
25
- next?: ParsedLocation;
26
- throwOnError?: boolean;
27
- __dehydratedMatches?: DehydratedRouteMatch[];
28
- }) => Promise<void>;
29
24
  export type BuildLocationFn<TRouteTree extends AnyRoute> = (opts: BuildNextOptions) => ParsedLocation;
30
25
  export type InjectedHtmlEntry = string | (() => Promise<string> | string);
31
26
  export declare const routerContext: React.Context<Router<any, Record<string, any>>>;
@@ -1,5 +1,5 @@
1
1
  import { ParsePathParams } from './link';
2
- import { AnyRoute, ResolveFullPath, ResolveFullSearchSchema, MergeFromFromParent, RouteContext, AnyContext, RouteOptions, UpdatableRouteOptions, Route, AnyPathParams, RootRouteId, TrimPathLeft, RouteConstraints } from './route';
2
+ import { AnyRoute, ResolveFullPath, ResolveFullSearchSchema, MergeFromFromParent, RouteContext, AnyContext, RouteOptions, UpdatableRouteOptions, Route, RootRouteId, TrimPathLeft, RouteConstraints } from './route';
3
3
  import { Assign, Expand, IsAny } from './utils';
4
4
  export interface FileRoutesByPath {
5
5
  }
@@ -13,7 +13,7 @@ export type FileRoutePath<TParentRoute extends AnyRoute, TFilePath extends strin
13
13
  export declare class FileRoute<TFilePath extends keyof FileRoutesByPath, TParentRoute extends AnyRoute = FileRoutesByPath[TFilePath]['parentRoute'], TId extends RouteConstraints['TId'] = TFilePath, TPath extends RouteConstraints['TPath'] = FileRoutePath<TParentRoute, TFilePath>, TFullPath extends RouteConstraints['TFullPath'] = ResolveFullPath<TParentRoute, RemoveUnderScores<TPath>>> {
14
14
  path: TFilePath;
15
15
  constructor(path: TFilePath);
16
- createRoute: <TSearchSchema extends import("./route").AnySearchSchema = {}, TFullSearchSchema extends import("./route").AnySearchSchema = ResolveFullSearchSchema<TParentRoute, TSearchSchema>, TParams extends Record<string, any> = ParsePathParams<TPath> extends never ? AnyPathParams : Record<ParsePathParams<TPath>, string>, TAllParams extends Record<string, any> = MergeFromFromParent<TParentRoute["types"]["allParams"], TParams>, TRouteContext extends RouteContext = RouteContext, TContext extends Expand<Assign<IsAny<TParentRoute["types"]["allContext"], {}>, TRouteContext>> = Expand<Assign<IsAny<TParentRoute["types"]["allContext"], {}>, TRouteContext>>, TRouterContext extends AnyContext = AnyContext, TLoaderData extends unknown = unknown, TChildren extends unknown = unknown, TRouteTree extends AnyRoute = AnyRoute>(options?: (Omit<RouteOptions<TParentRoute, string, string, TSearchSchema, TFullSearchSchema, TParams, TAllParams, TRouteContext, TContext, TLoaderData>, "path" | "id" | "getParentRoute"> & {
16
+ createRoute: <TSearchSchema extends import("./route").AnySearchSchema = {}, TFullSearchSchema extends import("./route").AnySearchSchema = ResolveFullSearchSchema<TParentRoute, TSearchSchema>, TParams extends Record<string, any> = Expand<Record<ParsePathParams<TPath>, string>>, TAllParams extends Record<string, any> = MergeFromFromParent<TParentRoute["types"]["allParams"], TParams>, TRouteContext extends RouteContext = RouteContext, TContext extends Expand<Assign<IsAny<TParentRoute["types"]["allContext"], {}>, TRouteContext>> = Expand<Assign<IsAny<TParentRoute["types"]["allContext"], {}>, TRouteContext>>, TRouterContext extends AnyContext = AnyContext, TLoaderData extends unknown = unknown, TChildren extends unknown = unknown, TRouteTree extends AnyRoute = AnyRoute>(options?: (Omit<RouteOptions<TParentRoute, string, TPath, TSearchSchema, TFullSearchSchema, TParams, TAllParams, TRouteContext, TContext, TLoaderData>, "path" | "id" | "getParentRoute"> & {
17
17
  meta?: import("./route").RouteMeta | undefined;
18
18
  } & {
19
19
  caseSensitive?: boolean | undefined;
@@ -29,7 +29,7 @@ export type MetaOptions = keyof PickRequired<RouteMeta> extends never ? {
29
29
  } : {
30
30
  meta: RouteMeta;
31
31
  };
32
- export type RouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, TSearchSchema extends Record<string, any> = {}, TFullSearchSchema extends Record<string, any> = TSearchSchema, TParams extends AnyPathParams = AnyPathParams, TAllParams extends AnyPathParams = TParams, TRouteContext extends RouteContext = RouteContext, TAllContext extends Record<string, any> = AnyContext, TLoaderData extends any = unknown> = BaseRouteOptions<TParentRoute, TCustomId, TPath, TSearchSchema, TFullSearchSchema, TParams, TAllParams, TRouteContext, TAllContext, TLoaderData> & NoInfer<UpdatableRouteOptions<TFullSearchSchema, TAllParams, TAllContext, TLoaderData>>;
32
+ export type RouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, TSearchSchema extends Record<string, any> = {}, TFullSearchSchema extends Record<string, any> = TSearchSchema, TParams extends AnyPathParams = AnyPathParams, TAllParams extends AnyPathParams = TParams, TRouteContext extends RouteContext = RouteContext, TAllContext extends Record<string, any> = AnyContext, TLoaderData extends any = unknown> = BaseRouteOptions<TParentRoute, TCustomId, TPath, TSearchSchema, TFullSearchSchema, TParams, TAllParams, TRouteContext, TAllContext, TLoaderData> & UpdatableRouteOptions<NoInfer<TFullSearchSchema>, NoInfer<TAllParams>, NoInfer<TAllContext>, NoInfer<TLoaderData>>;
33
33
  export type ParamsFallback<TPath extends string, TParams> = unknown extends TParams ? Record<ParsePathParams<TPath>, string> : TParams;
34
34
  export type BaseRouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, TSearchSchema extends Record<string, any> = {}, TFullSearchSchema extends Record<string, any> = TSearchSchema, TParams extends AnyPathParams = {}, TAllParams = ParamsFallback<TPath, TParams>, TRouteContext extends RouteContext = RouteContext, TAllContext extends Record<string, any> = AnyContext, TLoaderData extends any = unknown> = RoutePathOptions<TCustomId, TPath> & {
35
35
  getParentRoute: () => TParentRoute;
@@ -40,7 +40,11 @@ export type BaseRouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId
40
40
  } : {
41
41
  beforeLoad: BeforeLoadFn<TFullSearchSchema, TParentRoute, TAllParams, TRouteContext>;
42
42
  }) & {
43
- loader?: RouteLoadFn<TAllParams, TFullSearchSchema, NoInfer<TAllContext>, NoInfer<TRouteContext>, TLoaderData>;
43
+ key?: (opts: {
44
+ search: TFullSearchSchema;
45
+ location: ParsedLocation;
46
+ }) => any;
47
+ loader?: RouteLoaderFn<TAllParams, TFullSearchSchema, NoInfer<TAllContext>, NoInfer<TRouteContext>, TLoaderData>;
44
48
  } & ({
45
49
  parseParams?: (rawParams: IsAny<TPath, any, Record<ParsePathParams<TPath>, string>>) => TParams extends Record<ParsePathParams<TPath>, any> ? TParams : 'parseParams must return an object';
46
50
  stringifyParams?: (params: NoInfer<ParamsFallback<TPath, TParams>>) => Record<ParsePathParams<TPath>, string>;
@@ -87,7 +91,7 @@ export type DefinedPathParamWarning = 'Path params cannot be redefined by child
87
91
  export type ParentParams<TParentParams> = AnyPathParams extends TParentParams ? {} : {
88
92
  [Key in keyof TParentParams]?: DefinedPathParamWarning;
89
93
  };
90
- export type RouteLoadFn<TAllParams = {}, TFullSearchSchema extends Record<string, any> = {}, TAllContext extends Record<string, any> = AnyContext, TRouteContext extends Record<string, any> = AnyContext, TLoaderData extends any = unknown> = (match: LoaderFnContext<TAllParams, TFullSearchSchema, TAllContext, TRouteContext>) => Promise<TLoaderData> | TLoaderData;
94
+ export type RouteLoaderFn<TAllParams = {}, TFullSearchSchema extends Record<string, any> = {}, TAllContext extends Record<string, any> = AnyContext, TRouteContext extends Record<string, any> = AnyContext, TLoaderData extends any = unknown> = (match: LoaderFnContext<TAllParams, TFullSearchSchema, TAllContext, TRouteContext>) => Promise<TLoaderData> | TLoaderData;
91
95
  export interface LoaderFnContext<TAllParams = {}, TFullSearchSchema extends Record<string, any> = {}, TAllContext extends Record<string, any> = AnyContext, TRouteContext extends Record<string, any> = AnyContext> {
92
96
  abortController: AbortController;
93
97
  preload: boolean;
@@ -9,7 +9,7 @@ import { AnyRouteMatch, RouteMatch } from './Matches';
9
9
  import { ParsedLocation } from './location';
10
10
  import { LocationState } from './location';
11
11
  import { SearchSerializer, SearchParser } from './searchParams';
12
- import { BuildLinkFn, BuildLocationFn, CommitLocationOptions, InjectedHtmlEntry, LoadFn, MatchRouteFn, NavigateFn } from './RouterProvider';
12
+ import { BuildLinkFn, BuildLocationFn, CommitLocationOptions, InjectedHtmlEntry, MatchRouteFn, NavigateFn } from './RouterProvider';
13
13
  declare global {
14
14
  interface Window {
15
15
  __TSR_DEHYDRATED__?: HydrationCtx;
@@ -152,12 +152,16 @@ export declare class Router<TRouteTree extends AnyRoute = AnyRoute, TDehydrated
152
152
  commitLocation: ({ startTransition, ...next }: ParsedLocation & CommitLocationOptions) => Promise<void>;
153
153
  buildAndCommitLocation: ({ replace, resetScroll, startTransition, ...rest }?: BuildNextOptions & CommitLocationOptions) => Promise<void>;
154
154
  navigate: NavigateFn<TRouteTree>;
155
- loadMatches: ({ checkLatest, matches, preload, }: {
155
+ loadMatches: ({ checkLatest, matches, preload, invalidate, }: {
156
156
  checkLatest: () => Promise<void> | undefined;
157
157
  matches: AnyRouteMatch[];
158
158
  preload?: boolean | undefined;
159
+ invalidate?: boolean | undefined;
159
160
  }) => Promise<RouteMatch[]>;
160
- load: LoadFn;
161
+ invalidate: () => Promise<void>;
162
+ load: (opts?: {
163
+ invalidate?: boolean;
164
+ }) => Promise<void>;
161
165
  preloadRoute: (navigateOpts?: BuildNextOptions) => Promise<readonly [RouteMatch<AnyRoute, any>, RouteMatch<AnyRoute, any>[]]>;
162
166
  buildLink: BuildLinkFn<TRouteTree>;
163
167
  matchRoute: MatchRouteFn<TRouteTree>;
@@ -963,7 +963,6 @@
963
963
  matches: matches
964
964
  }) : null));
965
965
  }
966
- const defaultPending = () => null;
967
966
  function SafeFragment(props) {
968
967
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, props.children);
969
968
  }
@@ -978,9 +977,9 @@
978
977
  const routeId = match?.routeId;
979
978
  const route = routesById[routeId];
980
979
  const locationKey = useRouterState().location.state?.key;
981
- const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent ?? defaultPending;
980
+ const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent;
982
981
  const routeErrorComponent = route.options.errorComponent ?? options.defaultErrorComponent ?? ErrorComponent;
983
- const ResolvedSuspenseBoundary = route.options.wrapInSuspense ? React__namespace.Suspense : SafeFragment;
982
+ const ResolvedSuspenseBoundary = route.options.wrapInSuspense ?? PendingComponent ? React__namespace.Suspense : SafeFragment;
984
983
  const errorComponent = routeErrorComponent ? React__namespace.useCallback(props => {
985
984
  return /*#__PURE__*/React__namespace.createElement(routeErrorComponent, {
986
985
  ...props,
@@ -990,6 +989,7 @@
990
989
  useParams: route.useParams
991
990
  });
992
991
  }, [route]) : undefined;
992
+ const ResolvedCatchBoundary = errorComponent ? CatchBoundary : SafeFragment;
993
993
  return /*#__PURE__*/React__namespace.createElement(matchesContext.Provider, {
994
994
  value: matches
995
995
  }, /*#__PURE__*/React__namespace.createElement(ResolvedSuspenseBoundary, {
@@ -999,7 +999,7 @@
999
999
  useSearch: route.useSearch,
1000
1000
  useParams: route.useParams
1001
1001
  })
1002
- }, errorComponent ? /*#__PURE__*/React__namespace.createElement(CatchBoundary, {
1002
+ }, /*#__PURE__*/React__namespace.createElement(ResolvedCatchBoundary, {
1003
1003
  resetKey: locationKey,
1004
1004
  errorComponent: errorComponent,
1005
1005
  onCatch: () => {
@@ -1007,8 +1007,6 @@
1007
1007
  }
1008
1008
  }, /*#__PURE__*/React__namespace.createElement(MatchInner, {
1009
1009
  match: match
1010
- })) : /*#__PURE__*/React__namespace.createElement(SafeFragment, null, /*#__PURE__*/React__namespace.createElement(MatchInner, {
1011
- match: match
1012
1010
  }))));
1013
1011
  }
1014
1012
  function MatchInner({
@@ -1019,6 +1017,9 @@
1019
1017
  routesById
1020
1018
  } = useRouter();
1021
1019
  const route = routesById[match.routeId];
1020
+ if (match.id.split('/').length === 4) {
1021
+ console.log(match.id, pick(match, ['status', 'cause', 'isFetching']));
1022
+ }
1022
1023
  if (match.status === 'error') {
1023
1024
  throw match.error;
1024
1025
  }
@@ -1112,31 +1113,6 @@
1112
1113
  return typeof opts.select === 'function' ? opts.select(match?.loaderData) : match?.loaderData;
1113
1114
  }
1114
1115
 
1115
- // export type RouterContext<
1116
- // TRouteTree extends AnyRoute,
1117
- // // TDehydrated extends Record<string, any>,
1118
- // > = {
1119
- // buildLink: BuildLinkFn<TRouteTree>
1120
- // state: RouterState<TRouteTree>
1121
- // navigate: NavigateFn<TRouteTree>
1122
- // matchRoute: MatchRouteFn<TRouteTree>
1123
- // routeTree: TRouteTree
1124
- // routesById: RoutesById<TRouteTree>
1125
- // options: RouterOptions<TRouteTree>
1126
- // history: RouterHistory
1127
- // load: LoadFn
1128
- // buildLocation: BuildLocationFn<TRouteTree>
1129
- // subscribe: Router<TRouteTree>['subscribe']
1130
- // resetNextScrollRef: React.MutableRefObject<boolean>
1131
- // injectedHtmlRef: React.MutableRefObject<InjectedHtmlEntry[]>
1132
- // injectHtml: (entry: InjectedHtmlEntry) => void
1133
- // dehydrateData: <T>(
1134
- // key: any,
1135
- // getData: T | (() => Promise<T> | T),
1136
- // ) => () => void
1137
- // hydrateData: <T>(key: any) => T | undefined
1138
- // }
1139
-
1140
1116
  const routerContext = /*#__PURE__*/React__namespace.createContext(null);
1141
1117
  if (typeof document !== 'undefined') {
1142
1118
  window.__TSR_ROUTER_CONTEXT__ = routerContext;
@@ -1205,7 +1181,7 @@
1205
1181
  return () => {
1206
1182
  unsub();
1207
1183
  };
1208
- }, [history]);
1184
+ }, [router.history]);
1209
1185
  React__namespace.useLayoutEffect(() => {
1210
1186
  if (!isTransitioning && state.resolvedLocation !== state.location) {
1211
1187
  router.emit({
@@ -1810,38 +1786,66 @@
1810
1786
  }
1811
1787
  return;
1812
1788
  });
1813
- const matches = matchedRoutes.map((route, index) => {
1789
+ const matches = [];
1790
+ matchedRoutes.forEach((route, index) => {
1791
+ // Take each matched route and resolve + validate its search params
1792
+ // This has to happen serially because each route's search params
1793
+ // can depend on the parent route's search params
1794
+ // It must also happen before we create the match so that we can
1795
+ // pass the search params to the route's potential key function
1796
+ // which is used to uniquely identify the route match in state
1797
+
1798
+ const parentMatch = matches[index - 1];
1799
+ const [preMatchSearch, searchError] = (() => {
1800
+ // Validate the search params and stabilize them
1801
+ const parentSearch = parentMatch?.search ?? locationSearch;
1802
+ try {
1803
+ const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
1804
+ let search = validator?.(parentSearch) ?? {};
1805
+ return [{
1806
+ ...parentSearch,
1807
+ ...search
1808
+ }, undefined];
1809
+ } catch (err) {
1810
+ const searchError = new SearchParamError(err.message, {
1811
+ cause: err
1812
+ });
1813
+ if (opts?.throwOnError) {
1814
+ throw searchError;
1815
+ }
1816
+ return [parentSearch, searchError];
1817
+ }
1818
+ })();
1814
1819
  const interpolatedPath = interpolatePath(route.path, routeParams);
1815
- const matchId = interpolatePath(route.id, routeParams, true);
1820
+ const matchId = interpolatePath(route.id, routeParams, true) + (route.options.key?.({
1821
+ search: preMatchSearch,
1822
+ location: this.state.location
1823
+ }) ?? '');
1816
1824
 
1817
1825
  // Waste not, want not. If we already have a match for this route,
1818
1826
  // reuse it. This is important for layout routes, which might stick
1819
1827
  // around between navigation actions that only change leaf routes.
1820
1828
  const existingMatch = getRouteMatch(this.state, matchId);
1821
1829
  const cause = this.state.matches.find(d => d.id === matchId) ? 'stay' : 'enter';
1822
- if (existingMatch) {
1823
- return {
1824
- ...existingMatch,
1825
- cause
1826
- };
1827
- }
1828
1830
 
1829
1831
  // Create a fresh route match
1830
1832
  const hasLoaders = !!(route.options.loader || componentTypes.some(d => route.options[d]?.preload));
1831
- const routeMatch = {
1833
+ const match = existingMatch ? {
1834
+ ...existingMatch,
1835
+ cause
1836
+ } : {
1832
1837
  id: matchId,
1833
1838
  routeId: route.id,
1834
1839
  params: routeParams,
1835
1840
  pathname: joinPaths([this.basepath, interpolatedPath]),
1836
1841
  updatedAt: Date.now(),
1837
- routeSearch: {},
1838
1842
  search: {},
1843
+ searchError: undefined,
1839
1844
  status: hasLoaders ? 'pending' : 'success',
1840
1845
  isFetching: false,
1841
1846
  invalid: false,
1842
1847
  error: undefined,
1843
1848
  paramsError: parseErrors[index],
1844
- searchError: undefined,
1845
1849
  loadPromise: Promise.resolve(),
1846
1850
  context: undefined,
1847
1851
  abortController: new AbortController(),
@@ -1849,46 +1853,13 @@
1849
1853
  fetchedAt: 0,
1850
1854
  cause
1851
1855
  };
1852
- return routeMatch;
1853
- });
1854
1856
 
1855
- // Take each match and resolve its search params and context
1856
- // This has to happen after the matches are created or found
1857
- // so that we can use the parent match's search params and context
1858
- matches.forEach((match, i) => {
1859
- const parentMatch = matches[i - 1];
1860
- const route = this.looseRoutesById[match.routeId];
1861
- const searchInfo = (() => {
1862
- // Validate the search params and stabilize them
1863
- const parentSearchInfo = {
1864
- search: parentMatch?.search ?? locationSearch,
1865
- routeSearch: parentMatch?.routeSearch ?? locationSearch
1866
- };
1867
- try {
1868
- const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
1869
- let routeSearch = validator?.(parentSearchInfo.search) ?? {};
1870
- let search = {
1871
- ...parentSearchInfo.search,
1872
- ...routeSearch
1873
- };
1874
- routeSearch = replaceEqualDeep(match.routeSearch, routeSearch);
1875
- search = replaceEqualDeep(match.search, search);
1876
- return {
1877
- routeSearch,
1878
- search,
1879
- searchDidChange: match.routeSearch !== routeSearch
1880
- };
1881
- } catch (err) {
1882
- match.searchError = new SearchParamError(err.message, {
1883
- cause: err
1884
- });
1885
- if (opts?.throwOnError) {
1886
- throw match.searchError;
1887
- }
1888
- return parentSearchInfo;
1889
- }
1890
- })();
1891
- Object.assign(match, searchInfo);
1857
+ // Regardless of whether we're reusing an existing match or creating
1858
+ // a new one, we need to update the match's search params
1859
+ match.search = replaceEqualDeep(match.search, preMatchSearch);
1860
+ // And also update the searchError if there is one
1861
+ match.searchError = searchError;
1862
+ matches.push(match);
1892
1863
  });
1893
1864
  return matches;
1894
1865
  };
@@ -2082,7 +2053,8 @@
2082
2053
  loadMatches = async ({
2083
2054
  checkLatest,
2084
2055
  matches,
2085
- preload
2056
+ preload,
2057
+ invalidate
2086
2058
  }) => {
2087
2059
  let latestPromise;
2088
2060
  let firstBadMatchIndex;
@@ -2198,7 +2170,7 @@
2198
2170
  // Default to reloading the route all the time
2199
2171
  let shouldReload = true;
2200
2172
  let shouldReloadDeps = typeof route.options.shouldReload === 'function' ? route.options.shouldReload?.(loaderContext) : !!(route.options.shouldReload ?? true);
2201
- if (match.cause === 'enter') {
2173
+ if (match.cause === 'enter' || invalidate) {
2202
2174
  match.shouldReloadDeps = shouldReloadDeps;
2203
2175
  } else if (match.cause === 'stay') {
2204
2176
  if (typeof shouldReloadDeps === 'object') {
@@ -2281,7 +2253,10 @@
2281
2253
  await Promise.all(matchPromises);
2282
2254
  return matches;
2283
2255
  };
2284
- load = async () => {
2256
+ invalidate = () => this.load({
2257
+ invalidate: true
2258
+ });
2259
+ load = async opts => {
2285
2260
  const promise = new Promise(async (resolve, reject) => {
2286
2261
  const next = this.latestLocation;
2287
2262
  const prevLocation = this.state.resolvedLocation;
@@ -2316,7 +2291,8 @@
2316
2291
  // Load the matches
2317
2292
  await this.loadMatches({
2318
2293
  matches,
2319
- checkLatest: () => this.checkLatest(promise)
2294
+ checkLatest: () => this.checkLatest(promise),
2295
+ invalidate: opts?.invalidate
2320
2296
  });
2321
2297
  } catch (err) {
2322
2298
  // swallow this error, since we'll display the