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

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.
@@ -11,6 +11,7 @@ export interface RouteMatch<TRouteTree extends AnyRoute = AnyRoute, TRouteId ext
11
11
  params: RouteById<TRouteTree, TRouteId>['types']['allParams'];
12
12
  status: 'pending' | 'success' | 'error';
13
13
  isFetching: boolean;
14
+ showPending: boolean;
14
15
  invalid: boolean;
15
16
  error: unknown;
16
17
  paramsError: unknown;
@@ -19,8 +19,10 @@ export declare class FileRoute<TFilePath extends keyof FileRoutesByPath, TParent
19
19
  caseSensitive?: boolean | undefined;
20
20
  wrapInSuspense?: boolean | undefined;
21
21
  component?: import("./route").RouteComponent<TFullSearchSchema, TAllParams, TContext, TLoaderData> | undefined;
22
- errorComponent?: import("./route").ErrorRouteComponent<TFullSearchSchema, TAllParams, {}> | undefined;
22
+ errorComponent?: import("./route").ErrorRouteComponent<TFullSearchSchema, TAllParams, TContext> | undefined;
23
23
  pendingComponent?: import("./route").PendingRouteComponent<TFullSearchSchema, TAllParams, TContext> | undefined;
24
+ pendingMs?: number | undefined;
25
+ pendingMinMs?: number | undefined;
24
26
  preSearchFilters?: import("./route").SearchFilter<TFullSearchSchema, TFullSearchSchema>[] | undefined;
25
27
  postSearchFilters?: import("./route").SearchFilter<TFullSearchSchema, TFullSearchSchema>[] | undefined;
26
28
  onError?: ((err: any) => void) | undefined;
@@ -67,8 +67,10 @@ export type UpdatableRouteOptions<TFullSearchSchema extends Record<string, any>,
67
67
  caseSensitive?: boolean;
68
68
  wrapInSuspense?: boolean;
69
69
  component?: RouteComponent<TFullSearchSchema, TAllParams, TAllContext, TLoaderData>;
70
- errorComponent?: ErrorRouteComponent<TFullSearchSchema, TAllParams, {}>;
70
+ errorComponent?: ErrorRouteComponent<TFullSearchSchema, TAllParams, TAllContext>;
71
71
  pendingComponent?: PendingRouteComponent<TFullSearchSchema, TAllParams, TAllContext>;
72
+ pendingMs?: number;
73
+ pendingMinMs?: number;
72
74
  preSearchFilters?: SearchFilter<TFullSearchSchema>[];
73
75
  postSearchFilters?: SearchFilter<TFullSearchSchema>[];
74
76
  onError?: (err: any) => void;
@@ -40,16 +40,11 @@ export interface RouterOptions<TRouteTree extends AnyRoute, TDehydrated extends
40
40
  defaultComponent?: RouteComponent<AnySearchSchema, AnyPathParams, AnyContext>;
41
41
  defaultErrorComponent?: ErrorRouteComponent<AnySearchSchema, AnyPathParams, AnyContext>;
42
42
  defaultPendingComponent?: PendingRouteComponent<AnySearchSchema, AnyPathParams, AnyContext>;
43
- defaultMaxAge?: number;
44
- defaultGcMaxAge?: number;
45
- defaultPreloadMaxAge?: number;
43
+ defaultPendingMs?: number;
44
+ defaultPendingMinMs?: number;
46
45
  caseSensitive?: boolean;
47
46
  routeTree?: TRouteTree;
48
47
  basepath?: string;
49
- createRoute?: (opts: {
50
- route: AnyRoute;
51
- router: AnyRouter;
52
- }) => void;
53
48
  context?: TRouteTree['types']['routerContext'];
54
49
  routeMasks?: RouteMask<TRouteTree>[];
55
50
  unmaskOnReload?: boolean;
@@ -978,8 +978,14 @@
978
978
  const route = routesById[routeId];
979
979
  const locationKey = useRouterState().location.state?.key;
980
980
  const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent;
981
+ const pendingElement = PendingComponent ? /*#__PURE__*/React__namespace.createElement(PendingComponent, {
982
+ useMatch: route.useMatch,
983
+ useRouteContext: route.useRouteContext,
984
+ useSearch: route.useSearch,
985
+ useParams: route.useParams
986
+ }) : undefined;
981
987
  const routeErrorComponent = route.options.errorComponent ?? options.defaultErrorComponent ?? ErrorComponent;
982
- const ResolvedSuspenseBoundary = route.options.wrapInSuspense ?? PendingComponent ? React__namespace.Suspense : SafeFragment;
988
+ const ResolvedSuspenseBoundary = route.options.wrapInSuspense ?? pendingElement ? React__namespace.Suspense : SafeFragment;
983
989
  const errorComponent = routeErrorComponent ? React__namespace.useCallback(props => {
984
990
  return /*#__PURE__*/React__namespace.createElement(routeErrorComponent, {
985
991
  ...props,
@@ -993,12 +999,7 @@
993
999
  return /*#__PURE__*/React__namespace.createElement(matchesContext.Provider, {
994
1000
  value: matches
995
1001
  }, /*#__PURE__*/React__namespace.createElement(ResolvedSuspenseBoundary, {
996
- fallback: /*#__PURE__*/React__namespace.createElement(PendingComponent, {
997
- useMatch: route.useMatch,
998
- useRouteContext: route.useRouteContext,
999
- useSearch: route.useSearch,
1000
- useParams: route.useParams
1001
- })
1002
+ fallback: pendingElement
1002
1003
  }, /*#__PURE__*/React__namespace.createElement(ResolvedCatchBoundary, {
1003
1004
  resetKey: locationKey,
1004
1005
  errorComponent: errorComponent,
@@ -1006,24 +1007,26 @@
1006
1007
  warning(false, `Error in route match: ${match.id}`);
1007
1008
  }
1008
1009
  }, /*#__PURE__*/React__namespace.createElement(MatchInner, {
1009
- match: match
1010
+ match: match,
1011
+ pendingElement: pendingElement
1010
1012
  }))));
1011
1013
  }
1012
1014
  function MatchInner({
1013
- match
1015
+ match,
1016
+ pendingElement
1014
1017
  }) {
1015
1018
  const {
1016
1019
  options,
1017
1020
  routesById
1018
1021
  } = useRouter();
1019
1022
  const route = routesById[match.routeId];
1020
- if (match.id.split('/').length === 4) {
1021
- console.log(match.id, pick(match, ['status', 'cause', 'isFetching']));
1022
- }
1023
1023
  if (match.status === 'error') {
1024
1024
  throw match.error;
1025
1025
  }
1026
1026
  if (match.status === 'pending') {
1027
+ if (match.showPending) {
1028
+ return pendingElement || null;
1029
+ }
1027
1030
  throw match.loadPromise;
1028
1031
  }
1029
1032
  if (match.status === 'success') {
@@ -1566,6 +1569,8 @@
1566
1569
  constructor(options) {
1567
1570
  this.updateOptions({
1568
1571
  defaultPreloadDelay: 50,
1572
+ defaultPendingMs: 1000,
1573
+ defaultPendingMinMs: 500,
1569
1574
  context: undefined,
1570
1575
  ...options,
1571
1576
  stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
@@ -1842,6 +1847,7 @@
1842
1847
  search: {},
1843
1848
  searchError: undefined,
1844
1849
  status: hasLoaders ? 'pending' : 'success',
1850
+ showPending: false,
1845
1851
  isFetching: false,
1846
1852
  invalid: false,
1847
1853
  error: undefined,
@@ -2147,8 +2153,10 @@
2147
2153
  matches[index] = match = {
2148
2154
  ...match,
2149
2155
  fetchedAt: Date.now(),
2150
- invalid: false
2156
+ invalid: false,
2157
+ showPending: false
2151
2158
  };
2159
+ const pendingPromise = new Promise(r => setTimeout(r, 1000));
2152
2160
  if (match.isFetching) {
2153
2161
  loadPromise = getRouteMatch(this.state, match.id)?.loadPromise;
2154
2162
  } else {
@@ -2213,41 +2221,65 @@
2213
2221
  matches: s.matches.map(d => d.id === match.id ? match : d)
2214
2222
  }));
2215
2223
  }
2216
- try {
2217
- const loaderData = await loadPromise;
2218
- if (latestPromise = checkLatest()) return await latestPromise;
2219
- matches[index] = match = {
2220
- ...match,
2221
- error: undefined,
2222
- status: 'success',
2223
- isFetching: false,
2224
- updatedAt: Date.now(),
2225
- loaderData,
2226
- loadPromise: undefined
2227
- };
2228
- } catch (error) {
2229
- if (latestPromise = checkLatest()) return await latestPromise;
2230
- if (handleIfRedirect(error)) return;
2224
+ let didShowPending = false;
2225
+ await new Promise(async resolve => {
2226
+ // If the route has a pending component and a pendingMs option,
2227
+ // forcefully show the pending component
2228
+ if (!preload && (route.options.pendingComponent ?? this.options.defaultPendingComponent) && (route.options.pendingMs ?? this.options.defaultPendingMs)) {
2229
+ pendingPromise.then(() => {
2230
+ didShowPending = true;
2231
+ matches[index] = match = {
2232
+ ...match,
2233
+ showPending: true
2234
+ };
2235
+ this.setState(s => ({
2236
+ ...s,
2237
+ matches: s.matches.map(d => d.id === match.id ? match : d)
2238
+ }));
2239
+ resolve();
2240
+ });
2241
+ }
2231
2242
  try {
2232
- route.options.onError?.(error);
2233
- } catch (onErrorError) {
2234
- error = onErrorError;
2235
- if (handleIfRedirect(onErrorError)) return;
2243
+ const loaderData = await loadPromise;
2244
+ if (latestPromise = checkLatest()) return await latestPromise;
2245
+ const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
2246
+ if (didShowPending && pendingMinMs) {
2247
+ await new Promise(r => setTimeout(r, pendingMinMs));
2248
+ }
2249
+ matches[index] = match = {
2250
+ ...match,
2251
+ error: undefined,
2252
+ status: 'success',
2253
+ isFetching: false,
2254
+ updatedAt: Date.now(),
2255
+ loaderData,
2256
+ loadPromise: undefined
2257
+ };
2258
+ } catch (error) {
2259
+ if (latestPromise = checkLatest()) return await latestPromise;
2260
+ if (handleIfRedirect(error)) return;
2261
+ try {
2262
+ route.options.onError?.(error);
2263
+ } catch (onErrorError) {
2264
+ error = onErrorError;
2265
+ if (handleIfRedirect(onErrorError)) return;
2266
+ }
2267
+ matches[index] = match = {
2268
+ ...match,
2269
+ error,
2270
+ status: 'error',
2271
+ isFetching: false,
2272
+ updatedAt: Date.now()
2273
+ };
2236
2274
  }
2237
- matches[index] = match = {
2238
- ...match,
2239
- error,
2240
- status: 'error',
2241
- isFetching: false,
2242
- updatedAt: Date.now()
2243
- };
2244
- }
2245
- if (!preload) {
2246
- this.setState(s => ({
2247
- ...s,
2248
- matches: s.matches.map(d => d.id === match.id ? match : d)
2249
- }));
2250
- }
2275
+ if (!preload) {
2276
+ this.setState(s => ({
2277
+ ...s,
2278
+ matches: s.matches.map(d => d.id === match.id ? match : d)
2279
+ }));
2280
+ }
2281
+ resolve();
2282
+ });
2251
2283
  })());
2252
2284
  });
2253
2285
  await Promise.all(matchPromises);