or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

browser-integration.mdcache-management.mdclient-management.mdhydration.mdindex.mdinfinite-queries.mdmutations.mdquery-observers.mdquery-operations.mdutilities.md

utilities.mddocs/

0

# Utilities

1

2

Core utility functions for key hashing, data manipulation, query matching, functional programming patterns, and advanced data management operations.

3

4

## Capabilities

5

6

### Key Utilities

7

8

Functions for working with query and mutation keys.

9

10

```typescript { .api }

11

/**

12

* Create a stable hash from a query or mutation key

13

* Used internally for cache key generation and comparison

14

* @param key - Query or mutation key to hash

15

* @returns Stable string hash

16

*/

17

function hashKey(key: QueryKey | MutationKey): string;

18

19

/**

20

* Check if two query keys match partially

21

* Used for flexible query matching in filters

22

* @param a - First query key

23

* @param b - Second query key

24

* @returns true if keys match partially

25

*/

26

function partialMatchKey(a: QueryKey, b: QueryKey): boolean;

27

```

28

29

**Usage Examples:**

30

31

```typescript

32

import { hashKey, partialMatchKey } from "@tanstack/query-core";

33

34

// Hash keys for comparison

35

const hash1 = hashKey(['user', 123]);

36

const hash2 = hashKey(['user', 123]);

37

console.log(hash1 === hash2); // true

38

39

// Partial key matching

40

const matches1 = partialMatchKey(['user'], ['user', 123]); // true

41

const matches2 = partialMatchKey(['user', 123], ['user']); // false

42

const matches3 = partialMatchKey(['user', 123], ['user', 123]); // true

43

44

// Use in custom filtering

45

const findUserQueries = (userId?: number) => {

46

const queries = queryClient.getQueryCache().getAll();

47

return queries.filter(query => {

48

if (userId) {

49

return partialMatchKey(['user', userId], query.queryKey);

50

}

51

return partialMatchKey(['user'], query.queryKey);

52

});

53

};

54

```

55

56

### Query Matching

57

58

Functions for matching queries and mutations against filters.

59

60

```typescript { .api }

61

/**

62

* Check if a query matches the given filters

63

* Used internally by QueryClient methods for filtering operations

64

* @param filters - Query filters to match against

65

* @param query - Query instance to test

66

* @returns true if query matches all filters

67

*/

68

function matchQuery(filters: QueryFilters, query: Query): boolean;

69

70

/**

71

* Check if a mutation matches the given filters

72

* Used internally by QueryClient methods for filtering operations

73

* @param filters - Mutation filters to match against

74

* @param mutation - Mutation instance to test

75

* @returns true if mutation matches all filters

76

*/

77

function matchMutation(filters: MutationFilters, mutation: Mutation): boolean;

78

```

79

80

**Usage Examples:**

81

82

```typescript

83

import { matchQuery, matchMutation } from "@tanstack/query-core";

84

85

const queryCache = queryClient.getQueryCache();

86

const mutationCache = queryClient.getMutationCache();

87

88

// Custom query filtering

89

const customFilter = (query: Query) => {

90

return matchQuery({

91

queryKey: ['user'],

92

stale: true,

93

active: true,

94

}, query);

95

};

96

97

const matchingQueries = queryCache.getAll().filter(customFilter);

98

99

// Custom mutation filtering

100

const pendingUserMutations = mutationCache.getAll().filter(mutation => {

101

return matchMutation({

102

mutationKey: ['user'],

103

status: 'pending',

104

}, mutation);

105

});

106

107

// Advanced filtering logic

108

const complexQueryFilter = (query: Query) => {

109

// Match user queries that are either stale or have errors

110

const userMatch = matchQuery({ queryKey: ['user'] }, query);

111

const staleMatch = matchQuery({ stale: true }, query);

112

const errorMatch = matchQuery({ status: 'error' }, query);

113

114

return userMatch && (staleMatch || errorMatch);

115

};

116

```

117

118

### Data Manipulation

119

120

Functions for data transformation and structural sharing.

121

122

```typescript { .api }

123

/**

124

* Replace data with structural sharing optimization

125

* Preserves object references when data hasn't changed

126

* @param a - Previous data

127

* @param b - New data

128

* @returns Data with optimal structural sharing

129

*/

130

function replaceEqualDeep<T>(a: unknown, b: T): T;

131

132

/**

133

* Special function for keeping previous data during updates

134

* Used with placeholderData to maintain UI state during refetches

135

* @param previousData - The previous data to keep

136

* @returns The same data or undefined

137

*/

138

function keepPreviousData<T>(previousData: T | undefined): T | undefined;

139

```

140

141

**Usage Examples:**

142

143

```typescript

144

import { replaceEqualDeep, keepPreviousData } from "@tanstack/query-core";

145

146

// Structural sharing optimization

147

const oldUserData = { id: 1, name: 'John', settings: { theme: 'dark' } };

148

const newUserData = { id: 1, name: 'John', settings: { theme: 'dark' } };

149

150

const optimizedData = replaceEqualDeep(oldUserData, newUserData);

151

// optimizedData.settings === oldUserData.settings (same reference)

152

153

// Keep previous data during refetch

154

const userQuery = new QueryObserver(queryClient, {

155

queryKey: ['user', userId],

156

queryFn: fetchUser,

157

placeholderData: keepPreviousData,

158

});

159

160

// Custom data transformation with structural sharing

161

const transformData = (oldData, newData) => {

162

const transformed = {

163

...newData,

164

computed: newData.value * 2,

165

timestamp: Date.now(),

166

};

167

168

return replaceEqualDeep(oldData, transformed);

169

};

170

```

171

172

### Special Values and Tokens

173

174

Special values for conditional behavior and skipping operations.

175

176

```typescript { .api }

177

/**

178

* Special token used to skip query execution

179

* When used as queryFn, the query will not execute

180

*/

181

const skipToken: Symbol;

182

183

/**

184

* No-operation function

185

* Useful as a default or placeholder function

186

*/

187

function noop(): void;

188

189

/**

190

* Check if the current environment is server-side

191

* Useful for SSR-aware code

192

*/

193

const isServer: boolean;

194

```

195

196

**Usage Examples:**

197

198

```typescript

199

import { skipToken, noop, isServer } from "@tanstack/query-core";

200

201

// Conditional query execution

202

const userQuery = new QueryObserver(queryClient, {

203

queryKey: ['user', userId],

204

queryFn: userId ? fetchUser : skipToken, // Skip if no userId

205

});

206

207

// Skip query based on conditions

208

const conditionalQuery = new QueryObserver(queryClient, {

209

queryKey: ['data', id],

210

queryFn: shouldFetch ? fetchData : skipToken,

211

});

212

213

// No-op function usage

214

const defaultCallback = noop;

215

216

// Server-side detection

217

if (!isServer) {

218

// Client-side only code

219

setupBrowserEvents();

220

}

221

222

// SSR-aware query setup

223

const browserOnlyQuery = new QueryObserver(queryClient, {

224

queryKey: ['browser-data'],

225

queryFn: isServer ? skipToken : fetchBrowserData,

226

});

227

```

228

229

### Error Handling

230

231

Functions for error handling and decision making.

232

233

```typescript { .api }

234

/**

235

* Determine if an error should be thrown based on configuration

236

* Used internally to decide whether to throw errors or return them in state

237

* @param throwError - Error throwing configuration

238

* @param params - Error and query parameters

239

* @returns true if error should be thrown

240

*/

241

function shouldThrowError<TError, TData>(

242

throwError: ThrowOnError<TData, TError> | undefined,

243

params: [TError, Query<TData, TError>]

244

): boolean;

245

246

/**

247

* Check if a value is a cancelled error

248

* Exported from the retryer module

249

* @param value - Value to check

250

* @returns true if value is a CancelledError

251

*/

252

function isCancelledError(value: any): value is CancelledError;

253

254

/**

255

* Error class for cancelled operations

256

* Thrown when queries or mutations are cancelled

257

* Exported from the retryer module

258

*/

259

class CancelledError extends Error {

260

constructor(options?: { revert?: boolean; silent?: boolean });

261

revert?: boolean;

262

silent?: boolean;

263

}

264

```

265

266

**Usage Examples:**

267

268

```typescript

269

import { shouldThrowError, CancelledError, isCancelledError } from "@tanstack/query-core";

270

271

// Custom error handling logic

272

const customThrowError = (error, query) => {

273

// Only throw errors for specific query types

274

if (query.queryKey[0] === 'critical-data') {

275

return true;

276

}

277

278

// Don't throw network errors

279

if (error.name === 'NetworkError') {

280

return false;

281

}

282

283

return shouldThrowError(true, [error, query]);

284

};

285

286

// Use in query observer

287

const observer = new QueryObserver(queryClient, {

288

queryKey: ['data'],

289

queryFn: fetchData,

290

throwOnError: customThrowError,

291

});

292

293

// Handle cancelled errors

294

try {

295

await queryClient.fetchQuery({

296

queryKey: ['data'],

297

queryFn: fetchData,

298

});

299

} catch (error) {

300

if (isCancelledError(error)) {

301

console.log('Query was cancelled:', error.message);

302

303

if (error.revert) {

304

console.log('Should revert optimistic updates');

305

}

306

307

if (!error.silent) {

308

console.log('Should show error message');

309

}

310

} else {

311

console.error('Query failed:', error);

312

}

313

}

314

315

// Manual cancellation

316

const cancelQuery = () => {

317

throw new CancelledError({

318

revert: true,

319

silent: false

320

});

321

};

322

```

323

324

### Functional Programming Utilities

325

326

Type definitions for functional programming patterns and data updates.

327

328

```typescript { .api }

329

/**

330

* Type for functional updates

331

* Can be either a new value or a function that transforms the old value

332

*/

333

type Updater<T> = T | ((old: T) => T);

334

```

335

336

**Usage Examples:**

337

338

```typescript

339

// The Updater type is exported and used throughout the API

340

type Updater<T> = T | ((old: T) => T);

341

342

// Usage in query data updates

343

queryClient.setQueryData(['user', 123], (oldData) => ({

344

...oldData,

345

lastSeen: new Date().toISOString(),

346

}));

347

348

// Custom functional update utility

349

const updateUserData = (updater: Updater<UserData>) => {

350

const currentData = queryClient.getQueryData(['user', userId]);

351

const newData = typeof updater === 'function'

352

? updater(currentData)

353

: updater;

354

355

queryClient.setQueryData(['user', userId], newData);

356

};

357

358

// Usage

359

updateUserData({ name: 'New Name' }); // Direct value

360

updateUserData(old => ({ ...old, age: old.age + 1 })); // Function update

361

```

362

363

### Development and Debugging

364

365

Utilities for development and debugging scenarios.

366

367

```typescript { .api }

368

// Environment detection

369

if (!isServer) {

370

// Enable development tools

371

if (process.env.NODE_ENV === 'development') {

372

// Development-only code

373

window.__QUERY_CLIENT__ = queryClient;

374

}

375

}

376

377

// Debug helper for query cache

378

const debugQueryCache = () => {

379

const queries = queryClient.getQueryCache().getAll();

380

381

console.table(queries.map(query => ({

382

key: JSON.stringify(query.queryKey),

383

status: query.state.status,

384

fetchStatus: query.state.fetchStatus,

385

dataUpdatedAt: new Date(query.state.dataUpdatedAt).toLocaleString(),

386

observers: query.observers.length,

387

isStale: query.isStale(),

388

isActive: query.isActive(),

389

})));

390

};

391

392

// Debug helper for mutation cache

393

const debugMutationCache = () => {

394

const mutations = queryClient.getMutationCache().getAll();

395

396

console.table(mutations.map(mutation => ({

397

id: mutation.mutationId,

398

key: JSON.stringify(mutation.options.mutationKey),

399

status: mutation.state.status,

400

submittedAt: new Date(mutation.state.submittedAt).toLocaleString(),

401

variables: JSON.stringify(mutation.state.variables),

402

})));

403

};

404

405

// Performance monitoring

406

const monitorQueryPerformance = () => {

407

const cache = queryClient.getQueryCache();

408

409

cache.subscribe((event) => {

410

if (event.type === 'updated' && event.action.type === 'success') {

411

const duration = event.action.dataUpdatedAt - event.action.fetchedAt;

412

console.log(`Query ${JSON.stringify(event.query.queryKey)} took ${duration}ms`);

413

}

414

});

415

};

416

```

417

418

### Advanced Utility Patterns

419

420

Complex utility patterns for advanced use cases.

421

422

```typescript { .api }

423

// Query key factory pattern

424

const queryKeys = {

425

all: ['todos'] as const,

426

lists: () => [...queryKeys.all, 'list'] as const,

427

list: (filters: string) => [...queryKeys.lists(), { filters }] as const,

428

details: () => [...queryKeys.all, 'detail'] as const,

429

detail: (id: number) => [...queryKeys.details(), id] as const,

430

};

431

432

// Usage with utilities

433

const invalidateAllTodos = () => {

434

queryClient.invalidateQueries({ queryKey: queryKeys.all });

435

};

436

437

const invalidateListTodos = () => {

438

queryClient.invalidateQueries({ queryKey: queryKeys.lists() });

439

};

440

441

// Custom matcher utility

442

const createQueryMatcher = (baseKey: QueryKey) => {

443

return (query: Query): boolean => {

444

return partialMatchKey(baseKey, query.queryKey);

445

};

446

};

447

448

const todoMatcher = createQueryMatcher(['todos']);

449

const userMatcher = createQueryMatcher(['user']);

450

451

// Find queries using custom matchers

452

const todoQueries = queryClient.getQueryCache().getAll().filter(todoMatcher);

453

const userQueries = queryClient.getQueryCache().getAll().filter(userMatcher);

454

455

// Batch operations utility

456

const batchQueryOperations = (operations: Array<() => void>) => {

457

notifyManager.batch(() => {

458

operations.forEach(op => op());

459

});

460

};

461

462

// Usage

463

batchQueryOperations([

464

() => queryClient.setQueryData(['user', 1], userData1),

465

() => queryClient.setQueryData(['user', 2], userData2),

466

() => queryClient.invalidateQueries({ queryKey: ['posts'] }),

467

]);

468

```

469

470

## Core Types

471

472

```typescript { .api }

473

type QueryKey = ReadonlyArray<unknown>;

474

type MutationKey = ReadonlyArray<unknown>;

475

476

type Updater<T> = T | ((old: T) => T);

477

478

type ThrowOnError<TData, TError, TQueryData = TData, TQueryKey extends QueryKey = QueryKey> =

479

| boolean

480

| ((error: TError, query: Query<TQueryData, TError, TData, TQueryKey>) => boolean);

481

482

interface QueryFilters {

483

queryKey?: QueryKey;

484

exact?: boolean;

485

stale?: boolean;

486

predicate?: (query: Query) => boolean;

487

fetchStatus?: FetchStatus;

488

status?: QueryStatus;

489

type?: QueryTypeFilter;

490

}

491

492

interface MutationFilters {

493

mutationKey?: MutationKey;

494

exact?: boolean;

495

predicate?: (mutation: Mutation) => boolean;

496

status?: MutationStatus;

497

}

498

499

type QueryTypeFilter = 'all' | 'active' | 'inactive';

500

type FetchStatus = 'fetching' | 'paused' | 'idle';

501

type QueryStatus = 'pending' | 'error' | 'success';

502

type MutationStatus = 'idle' | 'pending' | 'success' | 'error';

503

```