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

browser-integration.mddocs/

0

# Browser Integration

1

2

Browser event management for automatic refetching on focus and network reconnection with customizable event handling, background sync, and visibility-based optimizations.

3

4

## Capabilities

5

6

### Focus Manager

7

8

Manages window focus state for automatic query refetching when the application regains focus.

9

10

```typescript { .api }

11

/**

12

* Global focus manager instance

13

* Handles window focus/blur events and triggers query refetches

14

*/

15

const focusManager: {

16

/**

17

* Set up custom focus event handling

18

* @param setup - Function to set up custom focus listeners

19

*/

20

setEventListener(setup: (setFocused: (focused?: boolean) => void) => () => void): void;

21

22

/**

23

* Manually set the focused state

24

* @param focused - Whether the application is focused (undefined = auto-detect)

25

*/

26

setFocused(focused?: boolean): void;

27

28

/**

29

* Trigger focus event manually

30

* Causes queries configured for refetchOnWindowFocus to refetch

31

*/

32

onFocus(): void;

33

34

/**

35

* Check if the application is currently focused

36

* @returns true if application has focus

37

*/

38

isFocused(): boolean;

39

};

40

```

41

42

**Usage Examples:**

43

44

```typescript

45

import { focusManager } from "@tanstack/query-core";

46

47

// Basic usage - uses default window focus events

48

console.log('Is focused:', focusManager.isFocused());

49

50

// Manual focus control

51

focusManager.setFocused(true);

52

focusManager.setFocused(false);

53

54

// Trigger refetch manually

55

focusManager.onFocus();

56

57

// Custom focus event handling

58

focusManager.setEventListener((setFocused) => {

59

// Custom logic for determining focus state

60

const handleVisibilityChange = () => {

61

setFocused(!document.hidden);

62

};

63

64

const handleFocus = () => setFocused(true);

65

const handleBlur = () => setFocused(false);

66

67

// Set up listeners

68

document.addEventListener('visibilitychange', handleVisibilityChange);

69

window.addEventListener('focus', handleFocus);

70

window.addEventListener('blur', handleBlur);

71

72

// Return cleanup function

73

return () => {

74

document.removeEventListener('visibilitychange', handleVisibilityChange);

75

window.removeEventListener('focus', handleFocus);

76

window.removeEventListener('blur', handleBlur);

77

};

78

});

79

80

// React Native focus handling

81

focusManager.setEventListener((setFocused) => {

82

const subscription = AppState.addEventListener('change', (state) => {

83

setFocused(state === 'active');

84

});

85

86

return () => subscription?.remove();

87

});

88

89

// Electron focus handling

90

focusManager.setEventListener((setFocused) => {

91

const { ipcRenderer } = require('electron');

92

93

const handleFocus = () => setFocused(true);

94

const handleBlur = () => setFocused(false);

95

96

ipcRenderer.on('focus', handleFocus);

97

ipcRenderer.on('blur', handleBlur);

98

99

return () => {

100

ipcRenderer.off('focus', handleFocus);

101

ipcRenderer.off('blur', handleBlur);

102

};

103

});

104

```

105

106

### Online Manager

107

108

Manages network connectivity state for automatic query refetching when connection is restored.

109

110

```typescript { .api }

111

/**

112

* Global online manager instance

113

* Handles network online/offline events and triggers query refetches

114

*/

115

const onlineManager: {

116

/**

117

* Set up custom online event handling

118

* @param setup - Function to set up custom online listeners

119

*/

120

setEventListener(setup: (setOnline: (online?: boolean) => void) => () => void): void;

121

122

/**

123

* Manually set the online state

124

* @param online - Whether the application is online (undefined = auto-detect)

125

*/

126

setOnline(online?: boolean): void;

127

128

/**

129

* Check if the application is currently online

130

* @returns true if application is online

131

*/

132

isOnline(): boolean;

133

};

134

```

135

136

**Usage Examples:**

137

138

```typescript

139

import { onlineManager } from "@tanstack/query-core";

140

141

// Basic usage - uses default navigator.onLine

142

console.log('Is online:', onlineManager.isOnline());

143

144

// Manual online control

145

onlineManager.setOnline(true);

146

onlineManager.setOnline(false);

147

148

// Custom online detection

149

onlineManager.setEventListener((setOnline) => {

150

const handleOnline = () => setOnline(true);

151

const handleOffline = () => setOnline(false);

152

153

// Set up listeners

154

window.addEventListener('online', handleOnline);

155

window.addEventListener('offline', handleOffline);

156

157

// Return cleanup function

158

return () => {

159

window.removeEventListener('online', handleOnline);

160

window.removeEventListener('offline', handleOffline);

161

};

162

});

163

164

// React Native network detection

165

onlineManager.setEventListener((setOnline) => {

166

const NetInfo = require('@react-native-async-storage/async-storage');

167

168

const unsubscribe = NetInfo.addEventListener((state) => {

169

setOnline(state.isConnected);

170

});

171

172

return unsubscribe;

173

});

174

175

// Advanced network quality detection

176

onlineManager.setEventListener((setOnline) => {

177

let isOnline = navigator.onLine;

178

179

// Test actual connectivity periodically

180

const testConnectivity = async () => {

181

try {

182

const response = await fetch('/api/ping', {

183

method: 'HEAD',

184

cache: 'no-cache',

185

});

186

const newOnlineState = response.ok;

187

if (newOnlineState !== isOnline) {

188

isOnline = newOnlineState;

189

setOnline(isOnline);

190

}

191

} catch {

192

if (isOnline) {

193

isOnline = false;

194

setOnline(false);

195

}

196

}

197

};

198

199

// Check connectivity every 30 seconds

200

const interval = setInterval(testConnectivity, 30000);

201

202

// Also listen to browser events

203

const handleOnline = () => {

204

isOnline = true;

205

setOnline(true);

206

testConnectivity(); // Verify actual connectivity

207

};

208

209

const handleOffline = () => {

210

isOnline = false;

211

setOnline(false);

212

};

213

214

window.addEventListener('online', handleOnline);

215

window.addEventListener('offline', handleOffline);

216

217

return () => {

218

clearInterval(interval);

219

window.removeEventListener('online', handleOnline);

220

window.removeEventListener('offline', handleOffline);

221

};

222

});

223

```

224

225

### Notification Manager

226

227

Manages notification batching and scheduling for optimal performance.

228

229

```typescript { .api }

230

/**

231

* Global notification manager instance

232

* Handles batching and scheduling of state updates

233

*/

234

const notifyManager: {

235

/**

236

* Batch multiple operations to reduce re-renders

237

* @param callback - Function containing operations to batch

238

* @returns Return value of the callback

239

*/

240

batch<T>(callback: () => T): T;

241

242

/**

243

* Create a batched version of a function

244

* @param callback - Function to make batched

245

* @returns Batched version of the function

246

*/

247

batchCalls<TArgs extends ReadonlyArray<unknown>, TResponse>(

248

callback: (...args: TArgs) => TResponse

249

): (...args: TArgs) => TResponse;

250

251

/**

252

* Schedule a notification to be processed

253

* @param callback - Function to schedule

254

*/

255

schedule(callback: () => void): void;

256

257

/**

258

* Set a custom notification function

259

* @param fn - Custom notification function

260

*/

261

setNotifyFunction(fn: (callback: () => void) => void): void;

262

263

/**

264

* Set a custom batch notification function

265

* @param fn - Custom batch notification function

266

*/

267

setBatchNotifyFunction(fn: (callback: () => void) => void): void;

268

269

/**

270

* Set a custom scheduler function

271

* @param fn - Custom scheduler function

272

*/

273

setScheduler(fn: (callback: () => void) => void): void;

274

};

275

276

/**

277

* Default scheduler function that uses setTimeout

278

*/

279

const defaultScheduler: (callback: () => void) => void;

280

```

281

282

**Usage Examples:**

283

284

```typescript

285

import { notifyManager, defaultScheduler } from "@tanstack/query-core";

286

287

// Batch multiple operations

288

notifyManager.batch(() => {

289

// Multiple state updates will be batched together

290

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

291

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

292

queryClient.setQueryData(['user', 3], userData3);

293

// Only one re-render will occur after this batch

294

});

295

296

// Create batched functions

297

const batchedUpdate = notifyManager.batchCalls((data) => {

298

// This function will be batched automatically

299

updateUI(data);

300

});

301

302

// Multiple calls will be batched

303

batchedUpdate(data1);

304

batchedUpdate(data2);

305

batchedUpdate(data3);

306

307

// Custom notification handling for React

308

notifyManager.setNotifyFunction((callback) => {

309

// Use React's batch updates

310

ReactDOM.unstable_batchedUpdates(callback);

311

});

312

313

// Custom scheduling with requestAnimationFrame

314

notifyManager.setScheduler((callback) => {

315

requestAnimationFrame(callback);

316

});

317

318

// Custom batch notification for performance

319

notifyManager.setBatchNotifyFunction((callback) => {

320

// Debounce notifications

321

clearTimeout(batchTimer);

322

batchTimer = setTimeout(callback, 0);

323

});

324

325

// React Concurrent Mode integration

326

notifyManager.setNotifyFunction((callback) => {

327

if (typeof React !== 'undefined' && React.startTransition) {

328

React.startTransition(callback);

329

} else {

330

callback();

331

}

332

});

333

```

334

335

### Background Sync Integration

336

337

Implementing background synchronization with browser APIs.

338

339

```typescript { .api }

340

// Service Worker integration for background sync

341

class BackgroundSyncManager {

342

private queryClient: QueryClient;

343

344

constructor(queryClient: QueryClient) {

345

this.queryClient = queryClient;

346

this.setupBackgroundSync();

347

}

348

349

private setupBackgroundSync() {

350

// Register service worker

351

if ('serviceWorker' in navigator) {

352

navigator.serviceWorker.register('/sw.js');

353

}

354

355

// Listen for focus events

356

focusManager.setEventListener((setFocused) => {

357

const handleVisibilityChange = () => {

358

const isVisible = !document.hidden;

359

setFocused(isVisible);

360

361

if (isVisible) {

362

// App became visible, sync data

363

this.syncOnForeground();

364

}

365

};

366

367

document.addEventListener('visibilitychange', handleVisibilityChange);

368

return () => document.removeEventListener('visibilitychange', handleVisibilityChange);

369

});

370

371

// Listen for online events

372

onlineManager.setEventListener((setOnline) => {

373

const handleOnline = () => {

374

setOnline(true);

375

this.syncOnReconnect();

376

};

377

378

const handleOffline = () => setOnline(false);

379

380

window.addEventListener('online', handleOnline);

381

window.addEventListener('offline', handleOffline);

382

383

return () => {

384

window.removeEventListener('online', handleOnline);

385

window.removeEventListener('offline', handleOffline);

386

};

387

});

388

}

389

390

private async syncOnForeground() {

391

// Refetch critical data when app comes to foreground

392

await this.queryClient.refetchQueries({

393

type: 'active',

394

stale: true,

395

});

396

}

397

398

private async syncOnReconnect() {

399

// Resume paused mutations and refetch failed queries

400

await Promise.all([

401

this.queryClient.resumePausedMutations(),

402

this.queryClient.refetchQueries({

403

predicate: (query) => query.state.fetchStatus === 'paused',

404

}),

405

]);

406

}

407

408

// Background sync for mutations

409

syncMutation(mutationKey: string, data: unknown) {

410

if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {

411

navigator.serviceWorker.controller.postMessage({

412

type: 'BACKGROUND_SYNC',

413

mutationKey,

414

data,

415

});

416

}

417

}

418

}

419

420

// Usage

421

const backgroundSync = new BackgroundSyncManager(queryClient);

422

```

423

424

### Page Lifecycle Integration

425

426

Advanced integration with Page Lifecycle API for better resource management.

427

428

```typescript { .api }

429

class PageLifecycleManager {

430

constructor(private queryClient: QueryClient) {

431

this.setupPageLifecycle();

432

}

433

434

private setupPageLifecycle() {

435

// Page Lifecycle API integration

436

if ('onfreeze' in document) {

437

document.addEventListener('freeze', this.handleFreeze.bind(this));

438

document.addEventListener('resume', this.handleResume.bind(this));

439

}

440

441

// Fallback for browsers without Page Lifecycle API

442

document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));

443

window.addEventListener('beforeunload', this.handleBeforeUnload.bind(this));

444

window.addEventListener('pagehide', this.handlePageHide.bind(this));

445

window.addEventListener('pageshow', this.handlePageShow.bind(this));

446

}

447

448

private handleFreeze() {

449

// Page is being frozen (backgrounded on mobile)

450

console.log('Page frozen - pausing queries');

451

452

// Cancel ongoing requests to save battery

453

this.queryClient.cancelQueries();

454

455

// Persist important cache data

456

const dehydratedState = dehydrate(this.queryClient);

457

try {

458

sessionStorage.setItem('query-cache-freeze', JSON.stringify(dehydratedState));

459

} catch (e) {

460

console.warn('Failed to persist cache on freeze');

461

}

462

}

463

464

private handleResume() {

465

// Page is being resumed

466

console.log('Page resumed - resuming queries');

467

468

// Restore cache if needed

469

try {

470

const stored = sessionStorage.getItem('query-cache-freeze');

471

if (stored) {

472

const state = JSON.parse(stored);

473

hydrate(this.queryClient, state);

474

sessionStorage.removeItem('query-cache-freeze');

475

}

476

} catch (e) {

477

console.warn('Failed to restore cache on resume');

478

}

479

480

// Refetch stale data

481

this.queryClient.refetchQueries({ stale: true });

482

}

483

484

private handleVisibilityChange() {

485

if (document.hidden) {

486

// Page hidden - reduce activity

487

this.reduceActivity();

488

} else {

489

// Page visible - resume normal activity

490

this.resumeActivity();

491

}

492

}

493

494

private handleBeforeUnload() {

495

// Page is about to unload - cleanup

496

this.queryClient.cancelQueries();

497

}

498

499

private handlePageHide(event: PageTransitionEvent) {

500

if (event.persisted) {

501

// Page is going into back/forward cache

502

this.handleFreeze();

503

}

504

}

505

506

private handlePageShow(event: PageTransitionEvent) {

507

if (event.persisted) {

508

// Page is coming back from back/forward cache

509

this.handleResume();

510

}

511

}

512

513

private reduceActivity() {

514

// Reduce query frequency when page is hidden

515

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

516

517

queries.forEach(query => {

518

if (query.observers.length === 0) {

519

// Stop background refetching for inactive queries

520

query.destroy();

521

}

522

});

523

}

524

525

private resumeActivity() {

526

// Resume normal query activity when page is visible

527

this.queryClient.refetchQueries({

528

type: 'active',

529

predicate: (query) => {

530

// Only refetch if data is stale or hasn't been fetched recently

531

const staleTime = query.options.staleTime ?? 0;

532

return Date.now() - query.state.dataUpdatedAt > staleTime;

533

},

534

});

535

}

536

}

537

538

// Usage

539

const lifecycleManager = new PageLifecycleManager(queryClient);

540

```

541

542

## Core Types

543

544

```typescript { .api }

545

type SetupFn = (setEventState: (state?: boolean) => void) => (() => void) | void;

546

547

interface NotifyFunction {

548

(callback: () => void): void;

549

}

550

551

interface BatchNotifyFunction {

552

(callback: () => void): void;

553

}

554

555

interface ScheduleFunction {

556

(callback: () => void): void;

557

}

558

```