- Spec files
npm-tanstack--react-router
Describes: pkg:npm/@tanstack/react-router@1.132.x
- 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 & Caching23Built-in data loading system with loaders, caching, invalidation, promise handling, and deferred data streaming for efficient data management.45## Capabilities67### Deferred Promises89Create deferred promises for streaming data loading and progressive rendering.1011```typescript { .api }12/**13* Create a deferred promise for streaming data14* @param promise - Promise to defer15* @returns Deferred promise wrapper for streaming16*/17function defer<T>(promise: Promise<T>): DeferredPromise<T>;1819interface 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}2526interface DeferredPromiseState<T> {27/** Current status of the promise */28status: "pending" | "success" | "error";29/** Resolved data if successful */30data?: T;31/** Error if promise rejected */32error?: any;33}3435/** Symbol key for deferred promise state */36declare const TSR_DEFERRED_PROMISE: unique symbol;37```3839**Usage Examples:**4041```typescript42import { defer } from "@tanstack/react-router";4344// In route loader - defer slow data45const Route = createRoute({46path: "/dashboard",47loader: async () => {48// Load fast data immediately49const user = await fetchUser();5051// Defer slow data for streaming52const analytics = defer(fetchAnalytics());53const reports = defer(fetchReports());5455return {56user, // Available immediately57analytics, // Streams in when ready58reports, // Streams in when ready59};60},61});6263// In component - handle deferred data64function Dashboard() {65const { user, analytics, reports } = Route.useLoaderData();6667return (68<div>69<h1>Welcome, {user.name}</h1>7071<Suspense fallback={<div>Loading analytics...</div>}>72<Await promise={analytics}>73{(data) => <AnalyticsChart data={data} />}74</Await>75</Suspense>7677<Suspense fallback={<div>Loading reports...</div>}>78<Await promise={reports}>79{(data) => <ReportsList reports={data} />}80</Await>81</Suspense>82</div>83);84}85```8687### Controlled Promises8889Create promises with external control for advanced async patterns.9091```typescript { .api }92/**93* Create a controllable promise with external resolve/reject94* @returns Promise with control methods95*/96function createControlledPromise<T>(): ControlledPromise<T>;9798interface ControlledPromise<T> extends Promise<T> {99/** Resolve the promise */100resolve: (value: T | PromiseLike<T>) => void;101/** Reject the promise */102reject: (reason?: any) => void;103/** Current status */104status: "pending" | "resolved" | "rejected";105}106```107108**Usage Examples:**109110```typescript111import { createControlledPromise } from "@tanstack/react-router";112113// Custom async operation with external control114function createAsyncOperation() {115const { promise, resolve, reject } = createControlledPromise<string>();116117// Simulate async operation118setTimeout(() => {119if (Math.random() > 0.5) {120resolve("Success!");121} else {122reject(new Error("Failed"));123}124}, 1000);125126return {127promise,128cancel: () => reject(new Error("Cancelled")),129forceSuccess: () => resolve("Forced success"),130};131}132133// In route loader134const Route = createRoute({135path: "/async-demo",136loader: () => {137const operation = createAsyncOperation();138return { result: defer(operation.promise) };139},140});141```142143### Loader Data Hooks144145Hooks for accessing loader data with type safety and selection.146147```typescript { .api }148/**149* Access loader data from route with type safety150* @param opts - Loader data access options151* @returns Loader data or selected subset152*/153function useLoaderData<154TRouter extends AnyRouter = RegisteredRouter,155TFrom extends RoutePaths<TRouter> = "/",156TStrict extends boolean = true,157TSelected = ResolveLoaderData<TRouter, TFrom>,158TStructuralSharing extends boolean = true159>(160opts?: {161from?: TFrom;162strict?: TStrict;163select?: (data: ResolveLoaderData<TRouter, TFrom>) => TSelected;164structuralSharing?: TStructuralSharing;165}166): UseLoaderDataResult<TRouter, TFrom, TStrict, TSelected>;167168/**169* Access loader dependencies170* @param opts - Loader deps access options171* @returns Loader dependencies172*/173function useLoaderDeps<174TRouter extends AnyRouter = RegisteredRouter,175TFrom extends RoutePaths<TRouter> = "/",176TStrict extends boolean = true,177TSelected = ResolveLoaderDeps<TRouter, TFrom>,178TStructuralSharing extends boolean = true179>(180opts?: {181from?: TFrom;182strict?: TStrict;183select?: (deps: ResolveLoaderDeps<TRouter, TFrom>) => TSelected;184structuralSharing?: TStructuralSharing;185}186): UseLoaderDepsResult<TRouter, TFrom, TStrict, TSelected>;187```188189**Usage Examples:**190191```typescript192import { useLoaderData, useLoaderDeps } from "@tanstack/react-router";193194// Access loader data in component195function PostDetail() {196// Get all loader data197const { post, comments, relatedPosts } = useLoaderData({198from: "/posts/$postId",199});200201// Select specific data202const postTitle = useLoaderData({203from: "/posts/$postId",204select: (data) => data.post.title,205});206207// Access loader deps for cache invalidation208const deps = useLoaderDeps({209from: "/posts/$postId",210});211212return (213<div>214<h1>{postTitle}</h1>215<div>{post.content}</div>216<CommentsList comments={comments} />217<RelatedPosts posts={relatedPosts} />218</div>219);220}221```222223### Awaited Data Hook224225Hook for handling deferred promises with suspense integration.226227```typescript { .api }228/**229* Handle deferred promises with suspense integration230* @param options - Await options231* @returns Tuple of resolved data and promise state232*/233function useAwaited<T>(options: AwaitOptions<T>): [T, DeferredPromise<T>];234235interface AwaitOptions<T> {236/** Promise to await */237promise: Promise<T> | DeferredPromise<T>;238}239```240241**Usage Examples:**242243```typescript244import { useAwaited, defer } from "@tanstack/react-router";245246function StreamingDataComponent() {247const { deferredAnalytics } = useLoaderData();248249try {250// This will suspend until promise resolves251const [analytics, promise] = useAwaited({ promise: deferredAnalytics });252253return (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 fallback261throw promise;262}263}264265// Alternative usage with error handling266function SafeStreamingComponent() {267const { deferredData } = useLoaderData();268269try {270const [data, promise] = useAwaited({ promise: deferredData });271272if (promise.__deferredState.status === "error") {273return <div>Error loading data: {promise.__deferredState.error.message}</div>;274}275276return <DataDisplay data={data} />;277} catch (promise) {278// Still loading279return <div>Loading...</div>;280}281}282```283284### Cache Invalidation285286Utilities for invalidating cached route data and triggering reloads.287288```typescript { .api }289/**290* Router invalidation method291* Invalidates all route matches and triggers reload292*/293interface Router {294/**295* Invalidate all route data and reload296* @returns Promise that resolves when invalidation completes297*/298invalidate(): Promise<void>;299300/**301* Preload a route's data302* @param options - Navigation options for route to preload303* @returns Promise that resolves when preloading completes304*/305preloadRoute<TFrom extends RoutePaths<TRouteTree> = "/">(306options: NavigateOptions<TRouteTree, TFrom>307): Promise<void>;308}309```310311**Usage Examples:**312313```typescript314import { useRouter } from "@tanstack/react-router";315316function DataManagement() {317const router = useRouter();318319const handleRefreshData = async () => {320// Invalidate all cached data and reload321await router.invalidate();322};323324const handlePreloadProfile = async () => {325// Preload profile data326await router.preloadRoute({ to: "/profile" });327};328329return (330<div>331<button onClick={handleRefreshData}>Refresh All Data</button>332<button onClick={handlePreloadProfile}>Preload Profile</button>333</div>334);335}336```337338### Route Loader Functions339340Types and utilities for defining route data loaders.341342```typescript { .api }343/**344* Route loader function type345*/346type RouteLoaderFn<TRoute extends AnyRoute = AnyRoute> = (347context: LoaderFnContext<TRoute>348) => any | Promise<any>;349350interface LoaderFnContext<TRoute extends AnyRoute = AnyRoute> {351/** Route parameters */352params: ResolveParams<TRoute>;353/** Search parameters */354search: InferFullSearchSchema<TRoute>;355/** Route context from beforeLoad */356context: RouteContext<TRoute>;357/** Current location */358location: ParsedLocation;359/** Abort signal for cancellation */360signal: AbortSignal;361/** Preload flag */362preload?: boolean;363}364365/**366* Loader data resolution types367*/368type ResolveLoaderData<TRouter extends AnyRouter, TFrom extends RoutePaths<TRouter>> =369RouteById<TRouter, TFrom>["loaderData"];370371type ResolveLoaderDeps<TRouter extends AnyRouter, TFrom extends RoutePaths<TRouter>> =372RouteById<TRouter, TFrom>["loaderDeps"];373```374375**Usage Examples:**376377```typescript378// Advanced loader with error handling and caching379const Route = createRoute({380path: "/api/data/$id",381loader: async ({ params, search, context, signal, preload }: LoaderFnContext) => {382// Check if this is a preload383if (preload) {384// Maybe return cached data for preloads385return getCachedData(params.id);386}387388// Use abort signal for cleanup389const controller = new AbortController();390signal.addEventListener("abort", () => controller.abort());391392try {393// Load multiple data sources394const [item, metadata] = await Promise.all([395fetchItem(params.id, { signal: controller.signal }),396fetchMetadata(params.id, { signal: controller.signal }),397]);398399// Apply search filters400const filteredData = applySearchFilters(item, search);401402// Use context for authorization403const authorizedData = await authorizeData(filteredData, context.user);404405return {406item: authorizedData,407metadata,408loadedAt: Date.now(),409};410} catch (error) {411if (error.name === "AbortError") {412throw new Error("Request cancelled");413}414throw error;415}416},417// Configure caching418staleTime: 5 * 60 * 1000, // 5 minutes419gcTime: 30 * 60 * 1000, // 30 minutes420});421```422423### Data Transformation424425Utilities for transforming and updating loader data.426427```typescript { .api }428/**429* Apply functional updates to data430* @param updater - Update function or new value431* @param previous - Previous value432* @returns Updated value433*/434function functionalUpdate<T>(435updater: T | ((prev: T) => T),436previous: T437): T;438439/**440* Deep equality replacement for structural sharing441* @param prev - Previous value442* @param next - Next value443* @returns Previous if equal, next if different444*/445function replaceEqualDeep<T>(prev: T, next: T): T;446```447448**Usage Examples:**449450```typescript451import { functionalUpdate, replaceEqualDeep } from "@tanstack/react-router";452453// Functional updates in loader454const Route = createRoute({455path: "/settings",456loader: async ({ context }) => {457const settings = await fetchSettings();458459// Apply functional update based on context460const updatedSettings = functionalUpdate(461(prev) => ({462...prev,463theme: context.user.preferredTheme,464}),465settings466);467468return { settings: updatedSettings };469},470});471472// Structural sharing for performance473function useOptimizedData() {474const data = useLoaderData();475476// Only re-render if data actually changed477const optimizedData = useMemo(() =>478replaceEqualDeep(previousData.current, data),479[data]480);481482return optimizedData;483}484```485486## Types487488### Loader Types489490```typescript { .api }491interface LoaderContext<TRoute extends AnyRoute = AnyRoute> {492params: ResolveParams<TRoute>;493search: InferFullSearchSchema<TRoute>;494context: RouteContext<TRoute>;495location: ParsedLocation;496signal: AbortSignal;497preload?: boolean;498}499500type LoaderData<TRoute extends AnyRoute = AnyRoute> = TRoute extends {501loader: infer TLoader;502}503? TLoader extends (...args: any[]) => infer TReturn504? TReturn extends Promise<infer TData>505? TData506: TReturn507: never508: {};509```510511### Promise State Types512513```typescript { .api }514interface DeferredPromiseState<T> {515status: "pending" | "success" | "error";516data?: T;517error?: any;518}519520type ControllablePromise<T> = Promise<T> & {521resolve: (value: T | PromiseLike<T>) => void;522reject: (reason?: any) => void;523status: "pending" | "resolved" | "rejected";524};525```526527### Cache Configuration Types528529```typescript { .api }530interface CacheOptions {531/** Time until data becomes stale */532staleTime?: number;533/** Time until data is garbage collected */534gcTime?: number;535/** Whether route should reload on focus */536shouldReload?: boolean | ((match: RouteMatch) => boolean);537}538```