or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdcore-query-hooks.mdindex.mdmutations.mdproviders-context.mdsuspense-integration.mdutilities.md

utilities.mddocs/

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