0
# Utility Hooks and Functions
1
2
This document covers utility hooks for state inspection, prefetching, and restoration management in @tanstack/react-query.
3
4
## State Inspection Hooks
5
6
### useIsFetching
7
8
Tracks the number of queries currently in fetching state, with optional filtering.
9
10
```typescript { .api }
11
function useIsFetching(
12
filters?: QueryFilters,
13
queryClient?: QueryClient
14
): number
15
```
16
17
**Parameters:**
18
- `filters` (optional): `QueryFilters` - Filters to limit which queries to count
19
- `queryClient` (optional): `QueryClient` - Custom QueryClient instance
20
21
**Returns:** `number` - Count of currently fetching queries
22
23
**Example:**
24
```typescript
25
import { useIsFetching } from '@tanstack/react-query'
26
27
function LoadingIndicator() {
28
const fetchingCount = useIsFetching()
29
30
if (fetchingCount > 0) {
31
return <div>Loading {fetchingCount} queries...</div>
32
}
33
34
return null
35
}
36
37
// With filters
38
function UserQueriesLoadingIndicator() {
39
const userFetchingCount = useIsFetching({
40
queryKey: ['users'],
41
exact: false
42
})
43
44
return userFetchingCount > 0 ? <div>Loading user data...</div> : null
45
}
46
```
47
48
### useIsMutating
49
50
Tracks the number of mutations currently in pending state, with optional filtering.
51
52
```typescript { .api }
53
function useIsMutating(
54
filters?: MutationFilters,
55
queryClient?: QueryClient
56
): number
57
```
58
59
**Parameters:**
60
- `filters` (optional): `MutationFilters` - Filters to limit which mutations to count
61
- `queryClient` (optional): `QueryClient` - Custom QueryClient instance
62
63
**Returns:** `number` - Count of currently pending mutations
64
65
**Example:**
66
```typescript
67
import { useIsMutating } from '@tanstack/react-query'
68
69
function MutationLoadingIndicator() {
70
const mutatingCount = useIsMutating()
71
72
if (mutatingCount > 0) {
73
return <div>Saving changes...</div>
74
}
75
76
return null
77
}
78
79
// With filters for specific mutation
80
function UserUpdateLoadingIndicator() {
81
const isUpdatingUser = useIsMutating({
82
mutationKey: ['updateUser']
83
})
84
85
return isUpdatingUser > 0 ? <div>Updating user...</div> : null
86
}
87
```
88
89
### useMutationState
90
91
Subscribes to the mutation cache and returns selected mutation states.
92
93
```typescript { .api }
94
function useMutationState<TResult = MutationState>(
95
options?: {
96
filters?: MutationFilters
97
select?: (mutation: Mutation) => TResult
98
},
99
queryClient?: QueryClient
100
): Array<TResult>
101
```
102
103
**Parameters:**
104
- `options` (optional): Configuration object
105
- `filters` (optional): `MutationFilters` - Filters for mutation selection
106
- `select` (optional): `(mutation: Mutation) => TResult` - Transform function for each mutation
107
- `queryClient` (optional): `QueryClient` - Custom QueryClient instance
108
109
**Returns:** `Array<TResult>` - Array of selected/transformed mutation states
110
111
**Example:**
112
```typescript
113
import { useMutationState } from '@tanstack/react-query'
114
115
function PendingMutations() {
116
const pendingMutations = useMutationState({
117
filters: { status: 'pending' },
118
select: (mutation) => ({
119
mutationKey: mutation.options.mutationKey,
120
submittedAt: mutation.state.submittedAt,
121
})
122
})
123
124
return (
125
<div>
126
{pendingMutations.map((mutation, index) => (
127
<div key={index}>
128
Mutation {mutation.mutationKey?.join(' ')} pending since{' '}
129
{new Date(mutation.submittedAt!).toLocaleTimeString()}
130
</div>
131
))}
132
</div>
133
)
134
}
135
136
// Get all mutation variables for failed mutations
137
function FailedMutationData() {
138
const failedVariables = useMutationState({
139
filters: { status: 'error' },
140
select: (mutation) => mutation.state.variables,
141
})
142
143
return <div>Failed mutations: {failedVariables.length}</div>
144
}
145
```
146
147
## Prefetch Hooks
148
149
### usePrefetchQuery
150
151
Prefetches a query during component render if it's not already in cache.
152
153
```typescript { .api }
154
function usePrefetchQuery<
155
TQueryFnData = unknown,
156
TError = DefaultError,
157
TData = TQueryFnData,
158
TQueryKey extends QueryKey = QueryKey
159
>(
160
options: UsePrefetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
161
queryClient?: QueryClient
162
): void
163
```
164
165
**Parameters:**
166
- `options`: `UsePrefetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>` - Query configuration for prefetching
167
- `queryClient` (optional): `QueryClient` - Custom QueryClient instance
168
169
**Returns:** `void`
170
171
**Key Features:**
172
- Only prefetches if query is not already in cache
173
- Runs during component render phase
174
- Uses same options as regular queries but optimized for prefetching
175
176
**Example:**
177
```typescript
178
import { usePrefetchQuery } from '@tanstack/react-query'
179
180
function UserProfilePage({ userId }: { userId: string }) {
181
// Prefetch user posts when viewing profile
182
usePrefetchQuery({
183
queryKey: ['users', userId, 'posts'],
184
queryFn: () => fetchUserPosts(userId),
185
staleTime: 5 * 60 * 1000, // 5 minutes
186
})
187
188
// Main profile query
189
const { data: user } = useQuery({
190
queryKey: ['users', userId],
191
queryFn: () => fetchUser(userId),
192
})
193
194
return (
195
<div>
196
<h1>{user?.name}</h1>
197
{/* Posts will be instantly available when navigating to posts tab */}
198
</div>
199
)
200
}
201
202
// Conditional prefetching
203
function ConditionalPrefetch({ shouldPrefetch, userId }: {
204
shouldPrefetch: boolean
205
userId: string
206
}) {
207
usePrefetchQuery({
208
queryKey: ['users', userId, 'settings'],
209
queryFn: () => fetchUserSettings(userId),
210
enabled: shouldPrefetch, // Only prefetch when needed
211
})
212
213
return <div>Component content</div>
214
}
215
```
216
217
### usePrefetchInfiniteQuery
218
219
Prefetches an infinite query during component render if it's not already in cache.
220
221
```typescript { .api }
222
function usePrefetchInfiniteQuery<
223
TQueryFnData = unknown,
224
TError = DefaultError,
225
TData = TQueryFnData,
226
TQueryKey extends QueryKey = QueryKey,
227
TPageParam = unknown
228
>(
229
options: FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
230
queryClient?: QueryClient
231
): void
232
```
233
234
**Parameters:**
235
- `options`: `FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>` - Infinite query configuration
236
- `queryClient` (optional): `QueryClient` - Custom QueryClient instance
237
238
**Returns:** `void`
239
240
**Key Features:**
241
- Prefetches only the first page by default
242
- Only runs if infinite query is not already in cache
243
- Useful for infinite lists that might be accessed soon
244
245
**Example:**
246
```typescript
247
import { usePrefetchInfiniteQuery } from '@tanstack/react-query'
248
249
function HomePage() {
250
// Prefetch first page of user posts for instant loading
251
usePrefetchInfiniteQuery({
252
queryKey: ['posts'],
253
queryFn: ({ pageParam = 0 }) => fetchPosts(pageParam),
254
getNextPageParam: (lastPage) => lastPage.nextCursor,
255
pages: 1, // Only prefetch first page
256
})
257
258
return (
259
<div>
260
<h1>Welcome</h1>
261
<Link to="/posts">View Posts</Link> {/* Will load instantly */}
262
</div>
263
)
264
}
265
266
// Prefetch with initial page param
267
function CategoryPage({ categoryId }: { categoryId: string }) {
268
usePrefetchInfiniteQuery({
269
queryKey: ['posts', { category: categoryId }],
270
queryFn: ({ pageParam = 0 }) => fetchPostsByCategory(categoryId, pageParam),
271
getNextPageParam: (lastPage) => lastPage.nextCursor,
272
initialPageParam: 0,
273
})
274
275
return <div>Category content</div>
276
}
277
```
278
279
## Restoration Management
280
281
### useIsRestoring
282
283
Indicates whether the app is currently in the hydration/restoration phase during SSR.
284
285
```typescript { .api }
286
function useIsRestoring(): boolean
287
```
288
289
**Returns:** `boolean` - `true` if currently restoring from server state, `false` otherwise
290
291
**Key Features:**
292
- Essential for SSR hydration scenarios
293
- Helps avoid hydration mismatches
294
- Used internally by suspense queries during hydration
295
296
**Example:**
297
```typescript
298
import { useIsRestoring } from '@tanstack/react-query'
299
300
function UserProfile() {
301
const isRestoring = useIsRestoring()
302
const { data: user, isPending } = useQuery({
303
queryKey: ['user'],
304
queryFn: fetchUser,
305
})
306
307
// Show different loading state during hydration
308
if (isRestoring) {
309
return <div>Restoring user data...</div>
310
}
311
312
if (isPending) {
313
return <div>Loading user...</div>
314
}
315
316
return <div>Welcome, {user.name}!</div>
317
}
318
319
// Conditional rendering during hydration
320
function ConditionalContent() {
321
const isRestoring = useIsRestoring()
322
323
// Don't render complex components during hydration
324
if (isRestoring) {
325
return <div>Loading app...</div>
326
}
327
328
return <ComplexInteractiveComponent />
329
}
330
331
// Custom hook for handling restoration state
332
function useHydrationSafeQuery(options: UseQueryOptions) {
333
const isRestoring = useIsRestoring()
334
335
return useQuery({
336
...options,
337
enabled: !isRestoring && (options.enabled ?? true),
338
})
339
}
340
```
341
342
## Type Definitions
343
344
```typescript { .api }
345
interface QueryFilters {
346
queryKey?: QueryKey
347
exact?: boolean
348
type?: 'active' | 'inactive' | 'all'
349
stale?: boolean
350
fetchStatus?: FetchStatus
351
predicate?: (query: Query) => boolean
352
}
353
354
interface MutationFilters {
355
mutationKey?: MutationKey
356
exact?: boolean
357
status?: MutationStatus
358
predicate?: (mutation: Mutation) => boolean
359
}
360
361
interface UsePrefetchQueryOptions<
362
TQueryFnData = unknown,
363
TError = DefaultError,
364
TData = TQueryFnData,
365
TQueryKey extends QueryKey = QueryKey
366
> extends Omit<FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'queryFn'> {
367
queryFn?: Exclude<
368
FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>['queryFn'],
369
SkipToken
370
>
371
}
372
```
373
374
## Core Utility Functions
375
376
The following utility functions are available from the core query system for advanced use cases:
377
378
### hashKey
379
380
Generates a deterministic hash string from a query key for internal use.
381
382
```typescript { .api }
383
function hashKey(queryKey: QueryKey): string
384
```
385
386
**Parameters:**
387
- `queryKey`: `QueryKey` - The query key to hash
388
389
**Returns:** `string` - Deterministic hash string
390
391
**Example:**
392
```typescript
393
import { hashKey } from '@tanstack/react-query'
394
395
const key1 = ['users', { id: 1 }]
396
const key2 = ['users', { id: 1 }]
397
const hash1 = hashKey(key1)
398
const hash2 = hashKey(key2)
399
400
console.log(hash1 === hash2) // true - same content produces same hash
401
```
402
403
### matchQuery
404
405
Checks if a query matches the provided filters.
406
407
```typescript { .api }
408
function matchQuery(
409
filters: QueryFilters,
410
query: Query
411
): boolean
412
```
413
414
**Parameters:**
415
- `filters`: `QueryFilters` - The filters to match against
416
- `query`: `Query` - The query instance to check
417
418
**Returns:** `boolean` - Whether the query matches the filters
419
420
### matchMutation
421
422
Checks if a mutation matches the provided filters.
423
424
```typescript { .api }
425
function matchMutation(
426
filters: MutationFilters,
427
mutation: Mutation
428
): boolean
429
```
430
431
**Parameters:**
432
- `filters`: `MutationFilters` - The filters to match against
433
- `mutation`: `Mutation` - The mutation instance to check
434
435
**Returns:** `boolean` - Whether the mutation matches the filters
436
437
### keepPreviousData
438
439
Utility function to keep previous data during refetches, useful for pagination.
440
441
```typescript { .api }
442
function keepPreviousData<T>(
443
previousData: T | undefined,
444
previousQuery: Query | undefined
445
): T | undefined
446
```
447
448
**Parameters:**
449
- `previousData`: `T | undefined` - The previous query data
450
- `previousQuery`: `Query | undefined` - The previous query instance
451
452
**Returns:** `T | undefined` - The data to use during refetch
453
454
**Example:**
455
```typescript
456
import { useQuery, keepPreviousData } from '@tanstack/react-query'
457
458
function PaginatedPosts({ page }: { page: number }) {
459
const { data, isPending, isPlaceholderData } = useQuery({
460
queryKey: ['posts', page],
461
queryFn: () => fetchPosts(page),
462
placeholderData: keepPreviousData,
463
})
464
465
return (
466
<div>
467
{data?.posts.map(post => <div key={post.id}>{post.title}</div>)}
468
{isPending && !isPlaceholderData && <div>Loading...</div>}
469
{isPlaceholderData && <div>Loading new page...</div>}
470
</div>
471
)
472
}
473
```
474
475
### skipToken
476
477
Special token that can be passed to queryFn to skip query execution.
478
479
```typescript { .api }
480
const skipToken: unique symbol
481
```
482
483
**Example:**
484
```typescript
485
import { useQuery, skipToken } from '@tanstack/react-query'
486
487
function ConditionalQuery({ userId, enabled }: {
488
userId: string | null
489
enabled: boolean
490
}) {
491
const { data } = useQuery({
492
queryKey: ['user', userId],
493
queryFn: enabled && userId ? () => fetchUser(userId) : skipToken,
494
})
495
496
return <div>{data?.name}</div>
497
}
498
```
499
500
### Error Utilities
501
502
#### CancelledError
503
504
Error class for cancelled query operations.
505
506
```typescript { .api }
507
class CancelledError extends Error {
508
constructor(options?: { revert?: boolean })
509
}
510
```
511
512
#### isCancelledError
513
514
Utility to check if an error is a cancellation error.
515
516
```typescript { .api }
517
function isCancelledError(value: any): value is CancelledError
518
```
519
520
**Example:**
521
```typescript
522
import { useMutation, isCancelledError } from '@tanstack/react-query'
523
524
function UpdateUser() {
525
const mutation = useMutation({
526
mutationFn: updateUser,
527
onError: (error) => {
528
if (isCancelledError(error)) {
529
console.log('Update was cancelled')
530
} else {
531
console.log('Update failed:', error.message)
532
}
533
}
534
})
535
536
return (
537
<button onClick={() => mutation.mutate(userData)}>
538
Update User
539
</button>
540
)
541
}
542
```
543
544
## Experimental APIs
545
546
### experimental_streamedQuery
547
548
**Experimental streaming query functionality for advanced use cases**
549
550
```typescript { .api }
551
const experimental_streamedQuery: unique symbol
552
```
553
554
⚠️ **Warning:** This is an experimental API that may change or be removed in future versions. Use with caution in production environments.
555
556
This experimental feature is designed for advanced streaming data scenarios and is subject to breaking changes. Refer to the official TanStack Query documentation for the latest information about experimental features.
557
558
## Error Handling
559
560
All utility hooks are designed to be safe and non-throwing:
561
562
- **State inspection hooks** return safe default values (0 for counts, empty arrays for collections)
563
- **Prefetch hooks** silently fail if queries cannot be prefetched
564
- **Restoration hooks** safely handle SSR/CSR transitions
565
566
**Common Error Scenarios:**
567
- Missing QueryClient context - hooks will use the default QueryClient or throw clear error messages
568
- Invalid filters - filters that don't match any queries simply return empty results
569
- Network failures during prefetch - silently ignored, main queries will handle errors when actually executed