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

query-observers.mddocs/

0

# Query Observers

1

2

Reactive observers for tracking query state changes with automatic updates, optimistic results, lifecycle management, and intelligent subscription handling.

3

4

## Capabilities

5

6

### QueryObserver

7

8

The primary observer class for tracking individual query state changes.

9

10

```typescript { .api }

11

/**

12

* Observer for tracking query state changes and managing subscriptions

13

* Provides reactive updates when query data, loading state, or errors change

14

*/

15

class QueryObserver<TData = unknown, TError = Error, TQueryData = TData, TQueryKey extends QueryKey = QueryKey> {

16

constructor(client: QueryClient, options: QueryObserverOptions<TData, TError, TQueryData, TQueryKey>);

17

18

/**

19

* Get the current result snapshot

20

* Returns the latest query state without subscribing

21

* @returns Current query observer result

22

*/

23

getCurrentResult(): QueryObserverResult<TData, TError>;

24

25

/**

26

* Get the current query instance

27

* @returns The underlying Query instance

28

*/

29

getCurrentQuery(): Query<TQueryData, TError, TData, TQueryKey>;

30

31

/**

32

* Subscribe to query state changes

33

* @param onStoreChange - Callback function called when state changes

34

* @returns Unsubscribe function

35

*/

36

subscribe(onStoreChange: (result: QueryObserverResult<TData, TError>) => void): () => void;

37

38

/**

39

* Update observer options

40

* @param options - New observer options to merge

41

*/

42

setOptions(options: QueryObserverOptions<TData, TError, TQueryData, TQueryKey>): void;

43

44

/**

45

* Get optimistic result for given options

46

* Useful for getting result before actually changing options

47

* @param options - Options to get optimistic result for

48

* @returns Optimistic query observer result

49

*/

50

getOptimisticResult(options: QueryObserverOptions<TData, TError, TQueryData, TQueryKey>): QueryObserverResult<TData, TError>;

51

52

/**

53

* Trigger a refetch of the query

54

* @param options - Refetch options

55

* @returns Promise resolving to the new result

56

*/

57

refetch(options?: RefetchOptions): Promise<QueryObserverResult<TData, TError>>;

58

59

/**

60

* Fetch with optimistic options

61

* @param options - Options for optimistic fetch

62

* @returns Promise resolving to the result

63

*/

64

fetchOptimistic(options: QueryObserverOptions<TData, TError, TQueryData, TQueryKey>): Promise<QueryObserverResult<TData, TError>>;

65

66

/**

67

* Destroy the observer and cleanup subscriptions

68

*/

69

destroy(): void;

70

71

/**

72

* Track specific properties of the result for selective updates

73

* @param result - Result to track

74

* @param onPropTracked - Callback when property is tracked

75

* @returns Tracked result

76

*/

77

trackResult(result: QueryObserverResult<TData, TError>, onPropTracked?: () => void): QueryObserverResult<TData, TError>;

78

79

/**

80

* Check if should refetch on window focus

81

* @returns true if should refetch on focus

82

*/

83

shouldFetchOnWindowFocus(): boolean;

84

85

/**

86

* Check if should refetch on reconnect

87

* @returns true if should refetch on reconnect

88

*/

89

shouldFetchOnReconnect(): boolean;

90

}

91

92

interface QueryObserverOptions<TData = unknown, TError = Error, TQueryData = TData, TQueryKey extends QueryKey = QueryKey> {

93

queryKey: TQueryKey;

94

queryFn?: QueryFunction<TQueryData, TQueryKey>;

95

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

96

networkMode?: NetworkMode;

97

initialData?: TData | InitialDataFunction<TData>;

98

initialDataUpdatedAt?: number | (() => number | undefined);

99

placeholderData?: TData | PlaceholderDataFunction<TData>;

100

staleTime?: number | ((query: Query<TQueryData, TError, TData, TQueryKey>) => number);

101

gcTime?: number;

102

refetchInterval?: number | false | ((data: TData | undefined, query: Query<TQueryData, TError, TData, TQueryKey>) => number | false);

103

refetchIntervalInBackground?: boolean;

104

refetchOnMount?: boolean | "always" | ((query: Query<TQueryData, TError, TData, TQueryKey>) => boolean | "always");

105

refetchOnWindowFocus?: boolean | "always" | ((query: Query<TQueryData, TError, TData, TQueryKey>) => boolean | "always");

106

refetchOnReconnect?: boolean | "always" | ((query: Query<TQueryData, TError, TData, TQueryKey>) => boolean | "always");

107

retry?: RetryValue<TError>;

108

retryDelay?: RetryDelayValue<TError>;

109

select?: (data: TQueryData) => TData;

110

throwOnError?: ThrowOnError<TQueryData, TError, TData, TQueryKey>;

111

meta?: QueryMeta;

112

structuralSharing?: boolean | ((oldData: TData | undefined, newData: TData) => TData);

113

}

114

115

interface QueryObserverResult<TData = unknown, TError = Error> {

116

data: TData | undefined;

117

dataUpdatedAt: number;

118

error: TError | null;

119

errorUpdatedAt: number;

120

failureCount: number;

121

failureReason: TError | null;

122

fetchStatus: FetchStatus;

123

isError: boolean;

124

isFetched: boolean;

125

isFetchedAfterMount: boolean;

126

isFetching: boolean;

127

isInitialLoading: boolean;

128

isLoading: boolean;

129

isLoadingError: boolean;

130

isPaused: boolean;

131

isPlaceholderData: boolean;

132

isPending: boolean;

133

isRefetching: boolean;

134

isRefetchError: boolean;

135

isStale: boolean;

136

isSuccess: boolean;

137

refetch: (options?: RefetchOptions) => Promise<QueryObserverResult<TData, TError>>;

138

status: QueryStatus;

139

}

140

```

141

142

**Usage Examples:**

143

144

```typescript

145

import { QueryClient, QueryObserver } from "@tanstack/query-core";

146

147

const queryClient = new QueryClient();

148

149

// Create observer

150

const observer = new QueryObserver(queryClient, {

151

queryKey: ['user', 123],

152

queryFn: async () => {

153

const response = await fetch('/api/user/123');

154

return response.json();

155

},

156

staleTime: 5 * 60 * 1000, // 5 minutes

157

refetchOnWindowFocus: true,

158

});

159

160

// Subscribe to changes

161

const unsubscribe = observer.subscribe((result) => {

162

console.log('Data:', result.data);

163

console.log('Loading:', result.isLoading);

164

console.log('Error:', result.error);

165

console.log('Status:', result.status);

166

167

if (result.isSuccess) {

168

console.log('User loaded successfully:', result.data);

169

}

170

171

if (result.isError) {

172

console.error('Failed to load user:', result.error);

173

}

174

});

175

176

// Get current result without subscribing

177

const currentResult = observer.getCurrentResult();

178

179

// Update options

180

observer.setOptions({

181

queryKey: ['user', 123],

182

queryFn: async () => {

183

const response = await fetch('/api/user/123?v=2');

184

return response.json();

185

},

186

staleTime: 10 * 60 * 1000, // 10 minutes

187

});

188

189

// Manual refetch

190

const newResult = await observer.refetch();

191

192

// Cleanup

193

unsubscribe();

194

observer.destroy();

195

```

196

197

### QueriesObserver

198

199

Observer for tracking multiple queries simultaneously.

200

201

```typescript { .api }

202

/**

203

* Observer for tracking multiple queries simultaneously

204

* Provides combined results and manages multiple query subscriptions

205

*/

206

class QueriesObserver<TCombinedResult = QueryObserverResult[]> {

207

constructor(

208

client: QueryClient,

209

queries: QueryObserverOptionsForCreateQueries[],

210

options?: QueriesObserverOptions<TCombinedResult>

211

);

212

213

/**

214

* Update the queries being observed

215

* @param queries - New array of query options

216

* @param options - Observer options

217

*/

218

setQueries(

219

queries: QueryObserverOptionsForCreateQueries[],

220

options?: QueriesObserverOptions<TCombinedResult>

221

): void;

222

223

/**

224

* Get current combined result from all queries

225

* @returns Combined result from all observed queries

226

*/

227

getCurrentResult(): TCombinedResult;

228

229

/**

230

* Get optimistic combined result for given queries

231

* @param queries - Query options to get optimistic result for

232

* @param options - Observer options

233

* @returns Optimistic combined result

234

*/

235

getOptimisticResult(

236

queries: QueryObserverOptionsForCreateQueries[],

237

options?: QueriesObserverOptions<TCombinedResult>

238

): TCombinedResult;

239

240

/**

241

* Subscribe to changes in any of the observed queries

242

* @param onStoreChange - Callback called when any query changes

243

* @returns Unsubscribe function

244

*/

245

subscribe(onStoreChange: (result: TCombinedResult) => void): () => void;

246

247

/**

248

* Destroy the observer and cleanup all subscriptions

249

*/

250

destroy(): void;

251

}

252

253

interface QueriesObserverOptions<TCombinedResult> {

254

combine?: (results: QueryObserverResult[]) => TCombinedResult;

255

}

256

257

type QueryObserverOptionsForCreateQueries = QueryObserverOptions & {

258

queryKey: QueryKey;

259

};

260

```

261

262

**Usage Examples:**

263

264

```typescript

265

import { QueryClient, QueriesObserver } from "@tanstack/query-core";

266

267

const queryClient = new QueryClient();

268

269

// Create queries observer

270

const queriesObserver = new QueriesObserver(queryClient, [

271

{

272

queryKey: ['user', 123],

273

queryFn: async () => {

274

const response = await fetch('/api/user/123');

275

return response.json();

276

},

277

},

278

{

279

queryKey: ['posts', 123],

280

queryFn: async () => {

281

const response = await fetch('/api/user/123/posts');

282

return response.json();

283

},

284

},

285

{

286

queryKey: ['settings', 123],

287

queryFn: async () => {

288

const response = await fetch('/api/user/123/settings');

289

return response.json();

290

},

291

},

292

], {

293

combine: (results) => ({

294

user: results[0],

295

posts: results[1],

296

settings: results[2],

297

isLoading: results.some(result => result.isLoading),

298

hasError: results.some(result => result.isError),

299

}),

300

});

301

302

// Subscribe to combined changes

303

const unsubscribe = queriesObserver.subscribe((result) => {

304

console.log('User data:', result.user.data);

305

console.log('Posts data:', result.posts.data);

306

console.log('Settings data:', result.settings.data);

307

console.log('Any loading:', result.isLoading);

308

console.log('Any error:', result.hasError);

309

});

310

311

// Update queries

312

queriesObserver.setQueries([

313

{

314

queryKey: ['user', 456], // Different user

315

queryFn: async () => {

316

const response = await fetch('/api/user/456');

317

return response.json();

318

},

319

},

320

{

321

queryKey: ['posts', 456],

322

queryFn: async () => {

323

const response = await fetch('/api/user/456/posts');

324

return response.json();

325

},

326

},

327

]);

328

329

// Cleanup

330

unsubscribe();

331

queriesObserver.destroy();

332

```

333

334

### Result Properties

335

336

Understanding the properties available in query observer results.

337

338

```typescript { .api }

339

interface QueryObserverResult<TData = unknown, TError = Error> {

340

/** The last successfully resolved data for the query */

341

data: TData | undefined;

342

343

/** The timestamp for when the query was most recently returned in a resolved state */

344

dataUpdatedAt: number;

345

346

/** The error object for the query, if an error was thrown */

347

error: TError | null;

348

349

/** The timestamp for when the query most recently returned the error status */

350

errorUpdatedAt: number;

351

352

/** The failure count for the query */

353

failureCount: number;

354

355

/** The failure reason for the query retry */

356

failureReason: TError | null;

357

358

/** The fetch status of the query */

359

fetchStatus: FetchStatus;

360

361

/** Will be true if the query has been fetched */

362

isFetched: boolean;

363

364

/** Will be true if the query has been fetched after the component mounted */

365

isFetchedAfterMount: boolean;

366

367

/** Will be true if the query is currently fetching */

368

isFetching: boolean;

369

370

/** Will be true if the query failed while fetching for the first time */

371

isLoadingError: boolean;

372

373

/** Will be true if the query is currently paused */

374

isPaused: boolean;

375

376

/** Will be true if the data shown is placeholder data */

377

isPlaceholderData: boolean;

378

379

/** Will be true if the query is currently refetching */

380

isRefetching: boolean;

381

382

/** Will be true if the query failed while refetching */

383

isRefetchError: boolean;

384

385

/** Will be true if the query data is stale */

386

isStale: boolean;

387

388

/** Derived booleans from status */

389

isError: boolean;

390

isInitialLoading: boolean;

391

isLoading: boolean;

392

isPending: boolean;

393

isSuccess: boolean;

394

395

/** Function to manually refetch the query */

396

refetch: (options?: RefetchOptions) => Promise<QueryObserverResult<TData, TError>>;

397

398

/** The status of the query */

399

status: QueryStatus;

400

}

401

```

402

403

### Observer Configuration

404

405

Advanced configuration options for query observers.

406

407

```typescript { .api }

408

type PlaceholderDataFunction<T> = (previousValue: T | undefined, previousQuery: Query | undefined) => T;

409

410

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

411

| boolean

412

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

413

414

interface RefetchOptions extends CancelOptions {

415

throwOnError?: boolean;

416

}

417

418

interface CancelOptions {

419

revert?: boolean;

420

silent?: boolean;

421

}

422

```

423

424

## Core Types

425

426

```typescript { .api }

427

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

428

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

429

type NetworkMode = 'online' | 'always' | 'offlineFirst';

430

431

type RetryValue<TError> = boolean | number | ((failureCount: number, error: TError) => boolean);

432

type RetryDelayValue<TError> = number | ((failureCount: number, error: TError, query: Query) => number);

433

434

type InitialDataFunction<T> = () => T | undefined;

435

436

interface QueryMeta extends Record<string, unknown> {}

437

```