npm-tanstack--react-router

Description
Modern and scalable routing for React applications with built-in data fetching, caching, and state management capabilities
Author
tessl
Last updated

How to use

npx @tessl/cli registry install tessl/npm-tanstack--react-router@1.132.0

data-loading.md docs/

1
# Data Loading & Caching
2
3
Built-in data loading system with loaders, caching, invalidation, promise handling, and deferred data streaming for efficient data management.
4
5
## Capabilities
6
7
### Deferred Promises
8
9
Create deferred promises for streaming data loading and progressive rendering.
10
11
```typescript { .api }
12
/**
13
* Create a deferred promise for streaming data
14
* @param promise - Promise to defer
15
* @returns Deferred promise wrapper for streaming
16
*/
17
function defer<T>(promise: Promise<T>): DeferredPromise<T>;
18
19
interface DeferredPromise<T> extends Promise<T> {
20
/** Deferred promise state marker */
21
[TSR_DEFERRED_PROMISE]: DeferredPromiseState<T>;
22
/** Access to deferred state */
23
__deferredState: DeferredPromiseState<T>;
24
}
25
26
interface DeferredPromiseState<T> {
27
/** Current status of the promise */
28
status: "pending" | "success" | "error";
29
/** Resolved data if successful */
30
data?: T;
31
/** Error if promise rejected */
32
error?: any;
33
}
34
35
/** Symbol key for deferred promise state */
36
declare const TSR_DEFERRED_PROMISE: unique symbol;
37
```
38
39
**Usage Examples:**
40
41
```typescript
42
import { defer } from "@tanstack/react-router";
43
44
// In route loader - defer slow data
45
const Route = createRoute({
46
path: "/dashboard",
47
loader: async () => {
48
// Load fast data immediately
49
const user = await fetchUser();
50
51
// Defer slow data for streaming
52
const analytics = defer(fetchAnalytics());
53
const reports = defer(fetchReports());
54
55
return {
56
user, // Available immediately
57
analytics, // Streams in when ready
58
reports, // Streams in when ready
59
};
60
},
61
});
62
63
// In component - handle deferred data
64
function Dashboard() {
65
const { user, analytics, reports } = Route.useLoaderData();
66
67
return (
68
<div>
69
<h1>Welcome, {user.name}</h1>
70
71
<Suspense fallback={<div>Loading analytics...</div>}>
72
<Await promise={analytics}>
73
{(data) => <AnalyticsChart data={data} />}
74
</Await>
75
</Suspense>
76
77
<Suspense fallback={<div>Loading reports...</div>}>
78
<Await promise={reports}>
79
{(data) => <ReportsList reports={data} />}
80
</Await>
81
</Suspense>
82
</div>
83
);
84
}
85
```
86
87
### Controlled Promises
88
89
Create promises with external control for advanced async patterns.
90
91
```typescript { .api }
92
/**
93
* Create a controllable promise with external resolve/reject
94
* @returns Promise with control methods
95
*/
96
function createControlledPromise<T>(): ControlledPromise<T>;
97
98
interface ControlledPromise<T> extends Promise<T> {
99
/** Resolve the promise */
100
resolve: (value: T | PromiseLike<T>) => void;
101
/** Reject the promise */
102
reject: (reason?: any) => void;
103
/** Current status */
104
status: "pending" | "resolved" | "rejected";
105
}
106
```
107
108
**Usage Examples:**
109
110
```typescript
111
import { createControlledPromise } from "@tanstack/react-router";
112
113
// Custom async operation with external control
114
function createAsyncOperation() {
115
const { promise, resolve, reject } = createControlledPromise<string>();
116
117
// Simulate async operation
118
setTimeout(() => {
119
if (Math.random() > 0.5) {
120
resolve("Success!");
121
} else {
122
reject(new Error("Failed"));
123
}
124
}, 1000);
125
126
return {
127
promise,
128
cancel: () => reject(new Error("Cancelled")),
129
forceSuccess: () => resolve("Forced success"),
130
};
131
}
132
133
// In route loader
134
const Route = createRoute({
135
path: "/async-demo",
136
loader: () => {
137
const operation = createAsyncOperation();
138
return { result: defer(operation.promise) };
139
},
140
});
141
```
142
143
### Loader Data Hooks
144
145
Hooks for accessing loader data with type safety and selection.
146
147
```typescript { .api }
148
/**
149
* Access loader data from route with type safety
150
* @param opts - Loader data access options
151
* @returns Loader data or selected subset
152
*/
153
function useLoaderData<
154
TRouter extends AnyRouter = RegisteredRouter,
155
TFrom extends RoutePaths<TRouter> = "/",
156
TStrict extends boolean = true,
157
TSelected = ResolveLoaderData<TRouter, TFrom>,
158
TStructuralSharing extends boolean = true
159
>(
160
opts?: {
161
from?: TFrom;
162
strict?: TStrict;
163
select?: (data: ResolveLoaderData<TRouter, TFrom>) => TSelected;
164
structuralSharing?: TStructuralSharing;
165
}
166
): UseLoaderDataResult<TRouter, TFrom, TStrict, TSelected>;
167
168
/**
169
* Access loader dependencies
170
* @param opts - Loader deps access options
171
* @returns Loader dependencies
172
*/
173
function useLoaderDeps<
174
TRouter extends AnyRouter = RegisteredRouter,
175
TFrom extends RoutePaths<TRouter> = "/",
176
TStrict extends boolean = true,
177
TSelected = ResolveLoaderDeps<TRouter, TFrom>,
178
TStructuralSharing extends boolean = true
179
>(
180
opts?: {
181
from?: TFrom;
182
strict?: TStrict;
183
select?: (deps: ResolveLoaderDeps<TRouter, TFrom>) => TSelected;
184
structuralSharing?: TStructuralSharing;
185
}
186
): UseLoaderDepsResult<TRouter, TFrom, TStrict, TSelected>;
187
```
188
189
**Usage Examples:**
190
191
```typescript
192
import { useLoaderData, useLoaderDeps } from "@tanstack/react-router";
193
194
// Access loader data in component
195
function PostDetail() {
196
// Get all loader data
197
const { post, comments, relatedPosts } = useLoaderData({
198
from: "/posts/$postId",
199
});
200
201
// Select specific data
202
const postTitle = useLoaderData({
203
from: "/posts/$postId",
204
select: (data) => data.post.title,
205
});
206
207
// Access loader deps for cache invalidation
208
const deps = useLoaderDeps({
209
from: "/posts/$postId",
210
});
211
212
return (
213
<div>
214
<h1>{postTitle}</h1>
215
<div>{post.content}</div>
216
<CommentsList comments={comments} />
217
<RelatedPosts posts={relatedPosts} />
218
</div>
219
);
220
}
221
```
222
223
### Awaited Data Hook
224
225
Hook for handling deferred promises with suspense integration.
226
227
```typescript { .api }
228
/**
229
* Handle deferred promises with suspense integration
230
* @param options - Await options
231
* @returns Tuple of resolved data and promise state
232
*/
233
function useAwaited<T>(options: AwaitOptions<T>): [T, DeferredPromise<T>];
234
235
interface AwaitOptions<T> {
236
/** Promise to await */
237
promise: Promise<T> | DeferredPromise<T>;
238
}
239
```
240
241
**Usage Examples:**
242
243
```typescript
244
import { useAwaited, defer } from "@tanstack/react-router";
245
246
function StreamingDataComponent() {
247
const { deferredAnalytics } = useLoaderData();
248
249
try {
250
// This will suspend until promise resolves
251
const [analytics, promise] = useAwaited({ promise: deferredAnalytics });
252
253
return (
254
<div>
255
<h2>Analytics (Status: {promise.__deferredState.status})</h2>
256
<AnalyticsChart data={analytics} />
257
</div>
258
);
259
} catch (promise) {
260
// Suspense boundary will catch and show fallback
261
throw promise;
262
}
263
}
264
265
// Alternative usage with error handling
266
function SafeStreamingComponent() {
267
const { deferredData } = useLoaderData();
268
269
try {
270
const [data, promise] = useAwaited({ promise: deferredData });
271
272
if (promise.__deferredState.status === "error") {
273
return <div>Error loading data: {promise.__deferredState.error.message}</div>;
274
}
275
276
return <DataDisplay data={data} />;
277
} catch (promise) {
278
// Still loading
279
return <div>Loading...</div>;
280
}
281
}
282
```
283
284
### Cache Invalidation
285
286
Utilities for invalidating cached route data and triggering reloads.
287
288
```typescript { .api }
289
/**
290
* Router invalidation method
291
* Invalidates all route matches and triggers reload
292
*/
293
interface Router {
294
/**
295
* Invalidate all route data and reload
296
* @returns Promise that resolves when invalidation completes
297
*/
298
invalidate(): Promise<void>;
299
300
/**
301
* Preload a route's data
302
* @param options - Navigation options for route to preload
303
* @returns Promise that resolves when preloading completes
304
*/
305
preloadRoute<TFrom extends RoutePaths<TRouteTree> = "/">(
306
options: NavigateOptions<TRouteTree, TFrom>
307
): Promise<void>;
308
}
309
```
310
311
**Usage Examples:**
312
313
```typescript
314
import { useRouter } from "@tanstack/react-router";
315
316
function DataManagement() {
317
const router = useRouter();
318
319
const handleRefreshData = async () => {
320
// Invalidate all cached data and reload
321
await router.invalidate();
322
};
323
324
const handlePreloadProfile = async () => {
325
// Preload profile data
326
await router.preloadRoute({ to: "/profile" });
327
};
328
329
return (
330
<div>
331
<button onClick={handleRefreshData}>Refresh All Data</button>
332
<button onClick={handlePreloadProfile}>Preload Profile</button>
333
</div>
334
);
335
}
336
```
337
338
### Route Loader Functions
339
340
Types and utilities for defining route data loaders.
341
342
```typescript { .api }
343
/**
344
* Route loader function type
345
*/
346
type RouteLoaderFn<TRoute extends AnyRoute = AnyRoute> = (
347
context: LoaderFnContext<TRoute>
348
) => any | Promise<any>;
349
350
interface LoaderFnContext<TRoute extends AnyRoute = AnyRoute> {
351
/** Route parameters */
352
params: ResolveParams<TRoute>;
353
/** Search parameters */
354
search: InferFullSearchSchema<TRoute>;
355
/** Route context from beforeLoad */
356
context: RouteContext<TRoute>;
357
/** Current location */
358
location: ParsedLocation;
359
/** Abort signal for cancellation */
360
signal: AbortSignal;
361
/** Preload flag */
362
preload?: boolean;
363
}
364
365
/**
366
* Loader data resolution types
367
*/
368
type ResolveLoaderData<TRouter extends AnyRouter, TFrom extends RoutePaths<TRouter>> =
369
RouteById<TRouter, TFrom>["loaderData"];
370
371
type ResolveLoaderDeps<TRouter extends AnyRouter, TFrom extends RoutePaths<TRouter>> =
372
RouteById<TRouter, TFrom>["loaderDeps"];
373
```
374
375
**Usage Examples:**
376
377
```typescript
378
// Advanced loader with error handling and caching
379
const Route = createRoute({
380
path: "/api/data/$id",
381
loader: async ({ params, search, context, signal, preload }: LoaderFnContext) => {
382
// Check if this is a preload
383
if (preload) {
384
// Maybe return cached data for preloads
385
return getCachedData(params.id);
386
}
387
388
// Use abort signal for cleanup
389
const controller = new AbortController();
390
signal.addEventListener("abort", () => controller.abort());
391
392
try {
393
// Load multiple data sources
394
const [item, metadata] = await Promise.all([
395
fetchItem(params.id, { signal: controller.signal }),
396
fetchMetadata(params.id, { signal: controller.signal }),
397
]);
398
399
// Apply search filters
400
const filteredData = applySearchFilters(item, search);
401
402
// Use context for authorization
403
const authorizedData = await authorizeData(filteredData, context.user);
404
405
return {
406
item: authorizedData,
407
metadata,
408
loadedAt: Date.now(),
409
};
410
} catch (error) {
411
if (error.name === "AbortError") {
412
throw new Error("Request cancelled");
413
}
414
throw error;
415
}
416
},
417
// Configure caching
418
staleTime: 5 * 60 * 1000, // 5 minutes
419
gcTime: 30 * 60 * 1000, // 30 minutes
420
});
421
```
422
423
### Data Transformation
424
425
Utilities for transforming and updating loader data.
426
427
```typescript { .api }
428
/**
429
* Apply functional updates to data
430
* @param updater - Update function or new value
431
* @param previous - Previous value
432
* @returns Updated value
433
*/
434
function functionalUpdate<T>(
435
updater: T | ((prev: T) => T),
436
previous: T
437
): T;
438
439
/**
440
* Deep equality replacement for structural sharing
441
* @param prev - Previous value
442
* @param next - Next value
443
* @returns Previous if equal, next if different
444
*/
445
function replaceEqualDeep<T>(prev: T, next: T): T;
446
```
447
448
**Usage Examples:**
449
450
```typescript
451
import { functionalUpdate, replaceEqualDeep } from "@tanstack/react-router";
452
453
// Functional updates in loader
454
const Route = createRoute({
455
path: "/settings",
456
loader: async ({ context }) => {
457
const settings = await fetchSettings();
458
459
// Apply functional update based on context
460
const updatedSettings = functionalUpdate(
461
(prev) => ({
462
...prev,
463
theme: context.user.preferredTheme,
464
}),
465
settings
466
);
467
468
return { settings: updatedSettings };
469
},
470
});
471
472
// Structural sharing for performance
473
function useOptimizedData() {
474
const data = useLoaderData();
475
476
// Only re-render if data actually changed
477
const optimizedData = useMemo(() =>
478
replaceEqualDeep(previousData.current, data),
479
[data]
480
);
481
482
return optimizedData;
483
}
484
```
485
486
## Types
487
488
### Loader Types
489
490
```typescript { .api }
491
interface LoaderContext<TRoute extends AnyRoute = AnyRoute> {
492
params: ResolveParams<TRoute>;
493
search: InferFullSearchSchema<TRoute>;
494
context: RouteContext<TRoute>;
495
location: ParsedLocation;
496
signal: AbortSignal;
497
preload?: boolean;
498
}
499
500
type LoaderData<TRoute extends AnyRoute = AnyRoute> = TRoute extends {
501
loader: infer TLoader;
502
}
503
? TLoader extends (...args: any[]) => infer TReturn
504
? TReturn extends Promise<infer TData>
505
? TData
506
: TReturn
507
: never
508
: {};
509
```
510
511
### Promise State Types
512
513
```typescript { .api }
514
interface DeferredPromiseState<T> {
515
status: "pending" | "success" | "error";
516
data?: T;
517
error?: any;
518
}
519
520
type ControllablePromise<T> = Promise<T> & {
521
resolve: (value: T | PromiseLike<T>) => void;
522
reject: (reason?: any) => void;
523
status: "pending" | "resolved" | "rejected";
524
};
525
```
526
527
### Cache Configuration Types
528
529
```typescript { .api }
530
interface CacheOptions {
531
/** Time until data becomes stale */
532
staleTime?: number;
533
/** Time until data is garbage collected */
534
gcTime?: number;
535
/** Whether route should reload on focus */
536
shouldReload?: boolean | ((match: RouteMatch) => boolean);
537
}
538
```