or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context.mderror-handling.mdindex.mdinfinite-queries.mdmutations.mdparallel-queries.mdqueries.mdssr.mdstatus.md

status.mddocs/

0

# Status Monitoring

1

2

Hooks for monitoring global query and mutation states across the application. These utilities help track loading states, show global loading indicators, and monitor application activity.

3

4

## Capabilities

5

6

### useIsFetching Hook

7

8

Monitor the number of queries currently fetching across the application.

9

10

```typescript { .api }

11

/**

12

* Get count of currently fetching queries

13

* @param filters - Optional filters to target specific queries

14

* @param options - Optional context configuration

15

* @returns Number of queries currently fetching

16

*/

17

function useIsFetching(filters?: QueryFilters, options?: ContextOptions): number;

18

19

/**

20

* Get count of currently fetching queries with queryKey filter

21

* @param queryKey - Query key to filter by

22

* @param filters - Additional filters

23

* @param options - Optional context configuration

24

* @returns Number of matching queries currently fetching

25

*/

26

function useIsFetching(

27

queryKey?: QueryKey,

28

filters?: QueryFilters,

29

options?: ContextOptions

30

): number;

31

```

32

33

**Usage Examples:**

34

35

```typescript

36

import { useIsFetching } from "react-query";

37

38

// Global loading indicator

39

function GlobalLoadingIndicator() {

40

const isFetching = useIsFetching();

41

42

if (isFetching) {

43

return (

44

<div className="global-loading">

45

<div className="spinner" />

46

Loading data... ({isFetching} active requests)

47

</div>

48

);

49

}

50

51

return null;

52

}

53

54

// Specific query type loading

55

function UserDataLoadingIndicator({ userId }: { userId: string }) {

56

const userQueriesFetching = useIsFetching({

57

queryKey: ['user', userId]

58

});

59

60

const isUserDataLoading = userQueriesFetching > 0;

61

62

return isUserDataLoading ? (

63

<div className="user-loading">Updating user data...</div>

64

) : null;

65

}

66

67

// Filter by query type

68

function PostsLoadingIndicator() {

69

const postsLoading = useIsFetching({

70

queryKey: ['posts'],

71

exact: false // Match any query starting with ['posts']

72

});

73

74

const analyticsLoading = useIsFetching({

75

queryKey: ['analytics'],

76

exact: false

77

});

78

79

return (

80

<div className="loading-status">

81

{postsLoading > 0 && <span>Posts loading...</span>}

82

{analyticsLoading > 0 && <span>Analytics loading...</span>}

83

</div>

84

);

85

}

86

```

87

88

### useIsMutating Hook

89

90

Monitor the number of mutations currently in progress across the application.

91

92

```typescript { .api }

93

/**

94

* Get count of currently running mutations

95

* @param filters - Optional filters to target specific mutations

96

* @param options - Optional context configuration

97

* @returns Number of mutations currently running

98

*/

99

function useIsMutating(

100

filters?: MutationFilters,

101

options?: ContextOptions

102

): number;

103

104

/**

105

* Get count of currently running mutations with mutationKey filter

106

* @param mutationKey - Mutation key to filter by

107

* @param filters - Additional filters excluding mutationKey

108

* @param options - Optional context configuration

109

* @returns Number of matching mutations currently running

110

*/

111

function useIsMutating(

112

mutationKey?: MutationKey,

113

filters?: Omit<MutationFilters, 'mutationKey'>,

114

options?: ContextOptions

115

): number;

116

```

117

118

**Usage Examples:**

119

120

```typescript

121

import { useIsMutating } from "react-query";

122

123

// Global mutation indicator

124

function GlobalSavingIndicator() {

125

const isMutating = useIsMutating();

126

127

if (isMutating) {

128

return (

129

<div className="global-saving">

130

<div className="pulse-dot" />

131

Saving changes... ({isMutating} operations)

132

</div>

133

);

134

}

135

136

return null;

137

}

138

139

// Specific mutation type

140

function UserUpdateIndicator({ userId }: { userId: string }) {

141

const isUpdatingUser = useIsMutating({

142

mutationKey: ['updateUser', userId]

143

});

144

145

return isUpdatingUser > 0 ? (

146

<div className="inline-saving">

147

<div className="spinner-small" />

148

Saving...

149

</div>

150

) : null;

151

}

152

153

// Multiple mutation types

154

function FormSavingStatus() {

155

const userMutations = useIsMutating({

156

mutationKey: ['user']

157

});

158

159

const settingsMutations = useIsMutating({

160

mutationKey: ['settings']

161

});

162

163

const profileMutations = useIsMutating({

164

mutationKey: ['profile']

165

});

166

167

const totalMutations = userMutations + settingsMutations + profileMutations;

168

169

if (totalMutations === 0) return null;

170

171

return (

172

<div className="form-status">

173

<div className="saving-indicator">

174

<span>Saving</span>

175

{userMutations > 0 && <span className="tag">User</span>}

176

{settingsMutations > 0 && <span className="tag">Settings</span>}

177

{profileMutations > 0 && <span className="tag">Profile</span>}

178

</div>

179

</div>

180

);

181

}

182

```

183

184

### Filter Types

185

186

Types for filtering queries and mutations in status monitoring.

187

188

```typescript { .api }

189

interface QueryFilters {

190

/** Query key to match */

191

queryKey?: QueryKey;

192

/** Whether to match query key exactly */

193

exact?: boolean;

194

/** Query type filter */

195

type?: 'active' | 'inactive' | 'all';

196

/** Whether to include stale queries */

197

stale?: boolean;

198

/** Whether to include fetching queries */

199

fetching?: boolean;

200

/** Custom predicate function */

201

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

202

}

203

204

interface MutationFilters {

205

/** Mutation key to match */

206

mutationKey?: MutationKey;

207

/** Whether to match mutation key exactly */

208

exact?: boolean;

209

/** Mutation type filter */

210

type?: 'active' | 'paused' | 'all';

211

/** Custom predicate function */

212

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

213

}

214

215

interface ContextOptions {

216

/** Custom React context to use */

217

context?: React.Context<QueryClient | undefined>;

218

}

219

220

type QueryKey = readonly unknown[];

221

type MutationKey = readonly unknown[];

222

```

223

224

## Advanced Usage Patterns

225

226

### Application-Wide Activity Monitor

227

228

Comprehensive activity monitoring across the entire application:

229

230

```typescript

231

function ApplicationActivityMonitor() {

232

const fetchingCount = useIsFetching();

233

const mutatingCount = useIsMutating();

234

235

const isActive = fetchingCount > 0 || mutatingCount > 0;

236

237

// Track activity for analytics

238

useEffect(() => {

239

if (isActive) {

240

analytics.track('app_activity_started', {

241

queries: fetchingCount,

242

mutations: mutatingCount

243

});

244

} else {

245

analytics.track('app_activity_stopped');

246

}

247

}, [isActive, fetchingCount, mutatingCount]);

248

249

// Prevent page unload during mutations

250

useEffect(() => {

251

if (mutatingCount > 0) {

252

const handleBeforeUnload = (e: BeforeUnloadEvent) => {

253

e.preventDefault();

254

e.returnValue = 'You have unsaved changes. Are you sure you want to leave?';

255

return e.returnValue;

256

};

257

258

window.addEventListener('beforeunload', handleBeforeUnload);

259

return () => window.removeEventListener('beforeunload', handleBeforeUnload);

260

}

261

}, [mutatingCount]);

262

263

return (

264

<div className="activity-monitor">

265

<div className="status-bar">

266

{fetchingCount > 0 && (

267

<div className="fetching-status">

268

<div className="loading-icon" />

269

{fetchingCount} loading

270

</div>

271

)}

272

273

{mutatingCount > 0 && (

274

<div className="mutating-status">

275

<div className="saving-icon" />

276

{mutatingCount} saving

277

</div>

278

)}

279

280

{!isActive && (

281

<div className="idle-status">

282

<div className="idle-icon" />

283

All up to date

284

</div>

285

)}

286

</div>

287

</div>

288

);

289

}

290

```

291

292

### Feature-Specific Loading States

293

294

Monitoring specific application features:

295

296

```typescript

297

interface FeatureLoadingStates {

298

dashboard: number;

299

profile: number;

300

notifications: number;

301

analytics: number;

302

}

303

304

function useFeatureLoadingStates(): FeatureLoadingStates {

305

const dashboardLoading = useIsFetching({

306

queryKey: ['dashboard'],

307

exact: false

308

});

309

310

const profileLoading = useIsFetching({

311

queryKey: ['profile'],

312

exact: false

313

}) + useIsMutating({

314

mutationKey: ['profile'],

315

exact: false

316

});

317

318

const notificationsLoading = useIsFetching({

319

queryKey: ['notifications'],

320

exact: false

321

});

322

323

const analyticsLoading = useIsFetching({

324

queryKey: ['analytics'],

325

exact: false

326

});

327

328

return {

329

dashboard: dashboardLoading,

330

profile: profileLoading,

331

notifications: notificationsLoading,

332

analytics: analyticsLoading

333

};

334

}

335

336

function FeatureStatusPanel() {

337

const loadingStates = useFeatureLoadingStates();

338

339

const features = [

340

{ name: 'Dashboard', count: loadingStates.dashboard },

341

{ name: 'Profile', count: loadingStates.profile },

342

{ name: 'Notifications', count: loadingStates.notifications },

343

{ name: 'Analytics', count: loadingStates.analytics }

344

];

345

346

return (

347

<div className="feature-status">

348

<h3>Feature Status</h3>

349

{features.map(feature => (

350

<div key={feature.name} className="feature-item">

351

<span>{feature.name}</span>

352

<span className={feature.count > 0 ? 'loading' : 'idle'}>

353

{feature.count > 0 ? `Loading (${feature.count})` : 'Ready'}

354

</span>

355

</div>

356

))}

357

</div>

358

);

359

}

360

```

361

362

### Performance Monitoring

363

364

Tracking query and mutation performance:

365

366

```typescript

367

function usePerformanceMonitoring() {

368

const [metrics, setMetrics] = useState({

369

activeQueries: 0,

370

activeMutations: 0,

371

peakQueries: 0,

372

peakMutations: 0,

373

totalQueries: 0,

374

totalMutations: 0

375

});

376

377

const currentQueries = useIsFetching();

378

const currentMutations = useIsMutating();

379

380

useEffect(() => {

381

setMetrics(prev => ({

382

...prev,

383

activeQueries: currentQueries,

384

activeMutations: currentMutations,

385

peakQueries: Math.max(prev.peakQueries, currentQueries),

386

peakMutations: Math.max(prev.peakMutations, currentMutations)

387

}));

388

}, [currentQueries, currentMutations]);

389

390

// Track when queries/mutations start and complete

391

const prevQueries = useRef(0);

392

const prevMutations = useRef(0);

393

394

useEffect(() => {

395

if (currentQueries > prevQueries.current) {

396

setMetrics(prev => ({

397

...prev,

398

totalQueries: prev.totalQueries + (currentQueries - prevQueries.current)

399

}));

400

}

401

prevQueries.current = currentQueries;

402

}, [currentQueries]);

403

404

useEffect(() => {

405

if (currentMutations > prevMutations.current) {

406

setMetrics(prev => ({

407

...prev,

408

totalMutations: prev.totalMutations + (currentMutations - prevMutations.current)

409

}));

410

}

411

prevMutations.current = currentMutations;

412

}, [currentMutations]);

413

414

return metrics;

415

}

416

417

function PerformancePanel() {

418

const metrics = usePerformanceMonitoring();

419

420

return (

421

<div className="performance-panel">

422

<h3>Performance Metrics</h3>

423

<div className="metrics-grid">

424

<div className="metric">

425

<label>Active Queries</label>

426

<span>{metrics.activeQueries}</span>

427

</div>

428

<div className="metric">

429

<label>Active Mutations</label>

430

<span>{metrics.activeMutations}</span>

431

</div>

432

<div className="metric">

433

<label>Peak Queries</label>

434

<span>{metrics.peakQueries}</span>

435

</div>

436

<div className="metric">

437

<label>Peak Mutations</label>

438

<span>{metrics.peakMutations}</span>

439

</div>

440

<div className="metric">

441

<label>Total Queries</label>

442

<span>{metrics.totalQueries}</span>

443

</div>

444

<div className="metric">

445

<label>Total Mutations</label>

446

<span>{metrics.totalMutations}</span>

447

</div>

448

</div>

449

</div>

450

);

451

}

452

```

453

454

### Conditional UI Based on Activity

455

456

Adapting UI behavior based on current activity:

457

458

```typescript

459

function AdaptiveUI({ children }: { children: React.ReactNode }) {

460

const isFetching = useIsFetching();

461

const isMutating = useIsMutating();

462

463

const isActive = isFetching > 0 || isMutating > 0;

464

const isHeavyActivity = isFetching > 3 || isMutating > 2;

465

466

return (

467

<div className={`app-container ${isActive ? 'activity-mode' : 'idle-mode'}`}>

468

{/* Reduce animations during heavy activity */}

469

<style>

470

{isHeavyActivity && `

471

.app-container * {

472

animation-duration: 0.1s !important;

473

transition-duration: 0.1s !important;

474

}

475

`}

476

</style>

477

478

{/* Activity overlay */}

479

{isActive && (

480

<div className="activity-overlay">

481

<div className="activity-indicator">

482

{isFetching > 0 && <span>Loading data...</span>}

483

{isMutating > 0 && <span>Saving changes...</span>}

484

</div>

485

</div>

486

)}

487

488

{/* Disable interactions during critical mutations */}

489

<div className={isMutating > 0 ? 'pointer-events-none' : ''}>

490

{children}

491

</div>

492

</div>

493

);

494

}

495

496

function NetworkStatusBar() {

497

const queryCount = useIsFetching();

498

const mutationCount = useIsMutating();

499

500

// Show different indicators based on activity type

501

if (mutationCount > 0) {

502

return (

503

<div className="status-bar saving">

504

<div className="icon-saving" />

505

<span>Saving {mutationCount} change{mutationCount !== 1 ? 's' : ''}...</span>

506

</div>

507

);

508

}

509

510

if (queryCount > 0) {

511

return (

512

<div className="status-bar loading">

513

<div className="icon-loading" />

514

<span>Loading {queryCount} request{queryCount !== 1 ? 's' : ''}...</span>

515

</div>

516

);

517

}

518

519

return (

520

<div className="status-bar idle">

521

<div className="icon-idle" />

522

<span>All data up to date</span>

523

</div>

524

);

525

}

526

```