0
# Cache Management
1
2
Low-level cache operations for direct query and mutation cache manipulation with events, filtering, batch operations, and advanced cache strategies.
3
4
## Capabilities
5
6
### QueryCache
7
8
Low-level cache for managing query instances and their lifecycle.
9
10
```typescript { .api }
11
/**
12
* Cache for managing Query instances
13
* Provides direct access to query storage and lifecycle management
14
*/
15
class QueryCache {
16
constructor(config?: QueryCacheConfig);
17
18
/**
19
* Build or retrieve a query from the cache
20
* Creates new query if it doesn't exist, otherwise returns existing
21
* @param client - QueryClient instance
22
* @param options - Query options
23
* @param state - Optional initial state
24
* @returns Query instance
25
*/
26
build<T>(client: QueryClient, options: QueryOptions<T>, state?: QueryState<T>): Query<T>;
27
28
/**
29
* Add a query to the cache
30
* @param query - Query instance to add
31
*/
32
add(query: Query): void;
33
34
/**
35
* Remove a query from the cache
36
* @param query - Query instance to remove
37
*/
38
remove(query: Query): void;
39
40
/**
41
* Clear all queries from the cache
42
*/
43
clear(): void;
44
45
/**
46
* Get a query by its hash
47
* @param queryHash - The query hash to look up
48
* @returns Query instance or undefined if not found
49
*/
50
get<T>(queryHash: string): Query<T> | undefined;
51
52
/**
53
* Get all queries in the cache
54
* @returns Array of all Query instances
55
*/
56
getAll(): Array<Query>;
57
58
/**
59
* Find the first query matching the filters
60
* @param filters - Query filters to match against
61
* @returns First matching Query instance or undefined
62
*/
63
find<T>(filters: QueryFilters): Query<T> | undefined;
64
65
/**
66
* Find all queries matching the filters
67
* @param filters - Query filters to match against
68
* @returns Array of matching Query instances
69
*/
70
findAll(filters?: QueryFilters): Array<Query>;
71
72
/**
73
* Subscribe to cache events
74
* @param callback - Callback function for cache events
75
* @returns Unsubscribe function
76
*/
77
subscribe(callback: (event: QueryCacheNotifyEvent) => void): () => void;
78
79
/**
80
* Notify cache listeners of an event
81
* @param event - Cache event to broadcast
82
*/
83
notify(event: QueryCacheNotifyEvent): void;
84
85
/**
86
* Handle window focus event
87
* Triggers refetch for queries configured to refetch on focus
88
*/
89
onFocus(): void;
90
91
/**
92
* Handle online event
93
* Triggers refetch for queries configured to refetch on reconnect
94
*/
95
onOnline(): void;
96
}
97
98
interface QueryCacheConfig {
99
/**
100
* Called when any query encounters an error
101
* @param error - The error that occurred
102
* @param query - The query that failed
103
*/
104
onError?: (error: Error, query: Query) => void;
105
106
/**
107
* Called when any query succeeds
108
* @param data - The data returned by the query
109
* @param query - The query that succeeded
110
*/
111
onSuccess?: (data: unknown, query: Query) => void;
112
113
/**
114
* Called when any query settles (success or error)
115
* @param data - The data returned (undefined if error)
116
* @param error - The error that occurred (null if success)
117
* @param query - The query that settled
118
*/
119
onSettled?: (data: unknown | undefined, error: Error | null, query: Query) => void;
120
}
121
```
122
123
**Usage Examples:**
124
125
```typescript
126
import { QueryCache, QueryClient } from "@tanstack/query-core";
127
128
// Create cache with event handlers
129
const queryCache = new QueryCache({
130
onError: (error, query) => {
131
console.log(`Query failed:`, query.queryKey, error);
132
133
// Log errors to monitoring service
134
errorReporting.captureException(error, {
135
tags: { queryKey: JSON.stringify(query.queryKey) },
136
});
137
},
138
onSuccess: (data, query) => {
139
console.log(`Query succeeded:`, query.queryKey);
140
},
141
onSettled: (data, error, query) => {
142
console.log(`Query settled:`, query.queryKey, { data, error });
143
},
144
});
145
146
const queryClient = new QueryClient({ queryCache });
147
148
// Subscribe to cache events
149
const unsubscribe = queryCache.subscribe((event) => {
150
switch (event.type) {
151
case 'added':
152
console.log('Query added:', event.query.queryKey);
153
break;
154
case 'removed':
155
console.log('Query removed:', event.query.queryKey);
156
break;
157
case 'updated':
158
console.log('Query updated:', event.query.queryKey, event.action);
159
break;
160
case 'observerAdded':
161
console.log('Observer added to query:', event.query.queryKey);
162
break;
163
case 'observerRemoved':
164
console.log('Observer removed from query:', event.query.queryKey);
165
break;
166
}
167
});
168
169
// Find queries by filters
170
const staleQueries = queryCache.findAll({ stale: true });
171
console.log(`Found ${staleQueries.length} stale queries`);
172
173
const userQueries = queryCache.findAll({
174
queryKey: ['user'],
175
exact: false, // Partial match
176
});
177
178
// Get specific query
179
const userQuery = queryCache.find({ queryKey: ['user', 123], exact: true });
180
if (userQuery) {
181
console.log('User query state:', userQuery.state);
182
}
183
184
// Clear cache
185
queryCache.clear();
186
187
// Cleanup
188
unsubscribe();
189
```
190
191
### MutationCache
192
193
Low-level cache for managing mutation instances and their execution.
194
195
```typescript { .api }
196
/**
197
* Cache for managing Mutation instances
198
* Handles mutation storage, scoping, and execution coordination
199
*/
200
class MutationCache {
201
constructor(config?: MutationCacheConfig);
202
203
/**
204
* Build or retrieve a mutation from the cache
205
* @param client - QueryClient instance
206
* @param options - Mutation options
207
* @param state - Optional initial state
208
* @returns Mutation instance
209
*/
210
build<T>(client: QueryClient, options: MutationOptions<T>, state?: MutationState<T>): Mutation<T>;
211
212
/**
213
* Add a mutation to the cache
214
* @param mutation - Mutation instance to add
215
*/
216
add(mutation: Mutation): void;
217
218
/**
219
* Remove a mutation from the cache
220
* @param mutation - Mutation instance to remove
221
*/
222
remove(mutation: Mutation): void;
223
224
/**
225
* Clear all mutations from the cache
226
*/
227
clear(): void;
228
229
/**
230
* Get all mutations in the cache
231
* @returns Array of all Mutation instances
232
*/
233
getAll(): Array<Mutation>;
234
235
/**
236
* Find the first mutation matching the filters
237
* @param filters - Mutation filters to match against
238
* @returns First matching Mutation instance or undefined
239
*/
240
find<T>(filters: MutationFilters): Mutation<T> | undefined;
241
242
/**
243
* Find all mutations matching the filters
244
* @param filters - Mutation filters to match against
245
* @returns Array of matching Mutation instances
246
*/
247
findAll(filters?: MutationFilters): Array<Mutation>;
248
249
/**
250
* Subscribe to cache events
251
* @param callback - Callback function for cache events
252
* @returns Unsubscribe function
253
*/
254
subscribe(callback: (event: MutationCacheNotifyEvent) => void): () => void;
255
256
/**
257
* Notify cache listeners of an event
258
* @param event - Cache event to broadcast
259
*/
260
notify(event: MutationCacheNotifyEvent): void;
261
262
/**
263
* Check if a mutation can run (respects scoping)
264
* @param mutation - Mutation to check
265
* @returns true if mutation can run
266
*/
267
canRun(mutation: Mutation): boolean;
268
269
/**
270
* Run the next mutation in the scope queue
271
* @param mutation - Mutation that just completed
272
* @returns Promise that resolves when next mutation runs
273
*/
274
runNext(mutation: Mutation): Promise<unknown>;
275
276
/**
277
* Resume all paused mutations
278
* @returns Promise that resolves when all mutations are resumed
279
*/
280
resumePausedMutations(): Promise<unknown>;
281
}
282
283
interface MutationCacheConfig {
284
/**
285
* Called when any mutation encounters an error
286
* @param error - The error that occurred
287
* @param variables - Variables passed to the mutation
288
* @param context - Context from onMutate
289
* @param mutation - The mutation that failed
290
*/
291
onError?: (error: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise<unknown> | unknown;
292
293
/**
294
* Called when any mutation succeeds
295
* @param data - The data returned by the mutation
296
* @param variables - Variables passed to the mutation
297
* @param context - Context from onMutate
298
* @param mutation - The mutation that succeeded
299
*/
300
onSuccess?: (data: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise<unknown> | unknown;
301
302
/**
303
* Called when any mutation settles
304
* @param data - The data returned (undefined if error)
305
* @param error - The error that occurred (null if success)
306
* @param variables - Variables passed to the mutation
307
* @param context - Context from onMutate
308
* @param mutation - The mutation that settled
309
*/
310
onSettled?: (data: unknown | undefined, error: unknown | null, variables: unknown, context: unknown, mutation: Mutation) => Promise<unknown> | unknown;
311
}
312
```
313
314
**Usage Examples:**
315
316
```typescript
317
import { MutationCache, QueryClient } from "@tanstack/query-core";
318
319
// Create cache with global mutation handlers
320
const mutationCache = new MutationCache({
321
onError: (error, variables, context, mutation) => {
322
console.error('Mutation failed:', mutation.options.mutationKey, error);
323
324
// Global error handling
325
if (error.status === 401) {
326
// Redirect to login
327
window.location.href = '/login';
328
}
329
},
330
onSuccess: (data, variables, context, mutation) => {
331
console.log('Mutation succeeded:', mutation.options.mutationKey);
332
333
// Global success tracking
334
analytics.track('mutation_success', {
335
mutationKey: mutation.options.mutationKey,
336
});
337
},
338
});
339
340
const queryClient = new QueryClient({ mutationCache });
341
342
// Subscribe to mutation events
343
const unsubscribe = mutationCache.subscribe((event) => {
344
switch (event.type) {
345
case 'added':
346
console.log('Mutation added:', event.mutation.mutationId);
347
break;
348
case 'removed':
349
console.log('Mutation removed:', event.mutation.mutationId);
350
break;
351
case 'updated':
352
console.log('Mutation updated:', event.mutation.mutationId, event.action);
353
break;
354
}
355
});
356
357
// Find pending mutations
358
const pendingMutations = mutationCache.findAll({
359
status: 'pending'
360
});
361
console.log(`${pendingMutations.length} mutations pending`);
362
363
// Check mutation queue status
364
const allMutations = mutationCache.getAll();
365
const queuedMutations = allMutations.filter(m => !mutationCache.canRun(m));
366
console.log(`${queuedMutations.length} mutations queued`);
367
368
// Resume paused mutations (e.g., after network reconnection)
369
await mutationCache.resumePausedMutations();
370
371
// Cleanup
372
unsubscribe();
373
```
374
375
### Cache Events
376
377
Understanding cache events for advanced cache monitoring and debugging.
378
379
```typescript { .api }
380
type QueryCacheNotifyEvent =
381
| QueryCacheNotifyEventAdded
382
| QueryCacheNotifyEventRemoved
383
| QueryCacheNotifyEventUpdated
384
| QueryCacheNotifyEventObserverAdded
385
| QueryCacheNotifyEventObserverRemoved
386
| QueryCacheNotifyEventObserverOptionsUpdated
387
| QueryCacheNotifyEventObserverResultsUpdated;
388
389
interface QueryCacheNotifyEventAdded {
390
type: 'added';
391
query: Query;
392
}
393
394
interface QueryCacheNotifyEventRemoved {
395
type: 'removed';
396
query: Query;
397
}
398
399
interface QueryCacheNotifyEventUpdated {
400
type: 'updated';
401
query: Query;
402
action: Action;
403
}
404
405
interface QueryCacheNotifyEventObserverAdded {
406
type: 'observerAdded';
407
query: Query;
408
observer: QueryObserver;
409
}
410
411
interface QueryCacheNotifyEventObserverRemoved {
412
type: 'observerRemoved';
413
query: Query;
414
observer: QueryObserver;
415
}
416
417
type MutationCacheNotifyEvent =
418
| MutationCacheNotifyEventAdded
419
| MutationCacheNotifyEventRemoved
420
| MutationCacheNotifyEventUpdated;
421
422
interface MutationCacheNotifyEventAdded {
423
type: 'added';
424
mutation: Mutation;
425
}
426
427
interface MutationCacheNotifyEventRemoved {
428
type: 'removed';
429
mutation: Mutation;
430
}
431
432
interface MutationCacheNotifyEventUpdated {
433
type: 'updated';
434
mutation: Mutation;
435
action: Action;
436
}
437
```
438
439
**Usage Examples:**
440
441
```typescript
442
// Advanced cache monitoring
443
const queryCache = new QueryCache();
444
445
queryCache.subscribe((event) => {
446
// Track query lifecycle
447
if (event.type === 'added') {
448
console.log(`New query created: ${JSON.stringify(event.query.queryKey)}`);
449
450
// Track memory usage
451
const totalQueries = queryCache.getAll().length;
452
console.log(`Total queries in cache: ${totalQueries}`);
453
}
454
455
if (event.type === 'updated') {
456
const { query, action } = event;
457
458
// Monitor specific query state changes
459
if (action.type === 'success') {
460
console.log(`Query succeeded: ${JSON.stringify(query.queryKey)}`);
461
console.log(`Data size: ${JSON.stringify(query.state.data).length} bytes`);
462
}
463
464
if (action.type === 'error') {
465
console.error(`Query failed: ${JSON.stringify(query.queryKey)}`, action.error);
466
}
467
}
468
469
if (event.type === 'observerAdded') {
470
console.log(`Observer added to: ${JSON.stringify(event.query.queryKey)}`);
471
console.log(`Total observers: ${event.query.observers.length}`);
472
}
473
});
474
```
475
476
### Cache Filters
477
478
Advanced filtering options for finding specific queries and mutations.
479
480
```typescript { .api }
481
interface QueryFilters {
482
/** Query key to match (exact or partial based on 'exact' flag) */
483
queryKey?: QueryKey;
484
485
/** Whether to match query key exactly */
486
exact?: boolean;
487
488
/** Filter by stale status */
489
stale?: boolean;
490
491
/** Filter by active status (has observers) */
492
active?: boolean;
493
494
/** Filter by inactive status (no observers) */
495
inactive?: boolean;
496
497
/** Filter by fetch status */
498
fetchStatus?: FetchStatus;
499
500
/** Filter by query status */
501
status?: QueryStatus;
502
503
/** Custom predicate function */
504
predicate?: (query: Query) => boolean;
505
506
/** Filter by query type */
507
type?: QueryTypeFilter;
508
}
509
510
interface MutationFilters {
511
/** Mutation key to match (exact or partial based on 'exact' flag) */
512
mutationKey?: MutationKey;
513
514
/** Whether to match mutation key exactly */
515
exact?: boolean;
516
517
/** Filter by mutation status */
518
status?: MutationStatus;
519
520
/** Custom predicate function */
521
predicate?: (mutation: Mutation) => boolean;
522
}
523
524
type QueryTypeFilter = 'all' | 'active' | 'inactive';
525
type FetchStatus = 'fetching' | 'paused' | 'idle';
526
type QueryStatus = 'pending' | 'error' | 'success';
527
type MutationStatus = 'idle' | 'pending' | 'success' | 'error';
528
```
529
530
**Usage Examples:**
531
532
```typescript
533
// Complex filtering examples
534
const queryCache = new QueryCache();
535
536
// Find all stale user queries
537
const staleUserQueries = queryCache.findAll({
538
queryKey: ['user'],
539
exact: false,
540
stale: true,
541
});
542
543
// Find queries that are currently fetching
544
const fetchingQueries = queryCache.findAll({
545
fetchStatus: 'fetching',
546
});
547
548
// Find inactive queries older than 1 hour
549
const oldInactiveQueries = queryCache.findAll({
550
inactive: true,
551
predicate: (query) => {
552
const oneHourAgo = Date.now() - 60 * 60 * 1000;
553
return query.state.dataUpdatedAt < oneHourAgo;
554
},
555
});
556
557
// Custom cleanup based on filters
558
const cleanupOldQueries = () => {
559
const queriesToRemove = queryCache.findAll({
560
predicate: (query) => {
561
// Remove queries older than 24 hours with no observers
562
const oneDayAgo = Date.now() - 24 * 60 * 60 * 1000;
563
return query.observers.length === 0 &&
564
query.state.dataUpdatedAt < oneDayAgo;
565
},
566
});
567
568
queriesToRemove.forEach(query => {
569
queryCache.remove(query);
570
});
571
572
console.log(`Cleaned up ${queriesToRemove.length} old queries`);
573
};
574
575
// Run cleanup periodically
576
setInterval(cleanupOldQueries, 60 * 60 * 1000); // Every hour
577
```
578
579
## Core Types
580
581
```typescript { .api }
582
interface QueryOptions<T> {
583
queryKey: QueryKey;
584
queryFn?: QueryFunction<T>;
585
enabled?: boolean;
586
networkMode?: NetworkMode;
587
retry?: RetryValue<T>;
588
retryDelay?: RetryDelayValue<T>;
589
staleTime?: number;
590
gcTime?: number;
591
meta?: QueryMeta;
592
initialData?: T;
593
initialDataUpdatedAt?: number;
594
structuralSharing?: boolean;
595
}
596
597
interface MutationOptions<T> {
598
mutationKey?: MutationKey;
599
mutationFn?: MutationFunction<T>;
600
retry?: RetryValue<T>;
601
retryDelay?: RetryDelayValue<T>;
602
networkMode?: NetworkMode;
603
meta?: MutationMeta;
604
scope?: { id: string };
605
}
606
607
type QueryKey = ReadonlyArray<unknown>;
608
type MutationKey = ReadonlyArray<unknown>;
609
type NetworkMode = 'online' | 'always' | 'offlineFirst';
610
```