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

context.mddocs/

0

# Context & Client Management

1

2

QueryClient provider system for sharing client instances across component trees with context isolation and configuration. This is the foundation that enables all React Query hooks to work.

3

4

## Capabilities

5

6

### QueryClientProvider Component

7

8

The root provider component that makes QueryClient available to all child components.

9

10

```typescript { .api }

11

/**

12

* Provides QueryClient instance to component tree

13

* @param props - Provider configuration with client and optional context settings

14

* @returns JSX element that wraps children with QueryClient context

15

*/

16

function QueryClientProvider(props: QueryClientProviderProps): JSX.Element;

17

18

type QueryClientProviderProps =

19

| QueryClientProviderPropsWithContext

20

| QueryClientProviderPropsWithContextSharing;

21

22

interface QueryClientProviderPropsWithContext {

23

/** QueryClient instance to provide */

24

client: QueryClient;

25

/** Child components that will have access to QueryClient */

26

children?: React.ReactNode;

27

/** Custom React context to use instead of default */

28

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

29

/** Context sharing must not be used with custom context */

30

contextSharing?: never;

31

}

32

33

interface QueryClientProviderPropsWithContextSharing {

34

/** QueryClient instance to provide */

35

client: QueryClient;

36

/** Child components that will have access to QueryClient */

37

children?: React.ReactNode;

38

/** Custom context cannot be used with context sharing */

39

context?: never;

40

/** Whether to share context across microfrontends/bundles */

41

contextSharing?: boolean;

42

}

43

```

44

45

**Usage Examples:**

46

47

```typescript

48

import { QueryClient, QueryClientProvider } from "react-query";

49

50

// Basic setup

51

const queryClient = new QueryClient();

52

53

function App() {

54

return (

55

<QueryClientProvider client={queryClient}>

56

<div className="App">

57

<Header />

58

<MainContent />

59

<Footer />

60

</div>

61

</QueryClientProvider>

62

);

63

}

64

65

// With configuration

66

const queryClient = new QueryClient({

67

defaultOptions: {

68

queries: {

69

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

70

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

71

retry: 2,

72

refetchOnWindowFocus: false

73

},

74

mutations: {

75

retry: 1

76

}

77

}

78

});

79

80

function AppWithDefaults() {

81

return (

82

<QueryClientProvider client={queryClient}>

83

<Router>

84

<Routes>

85

<Route path="/" element={<Home />} />

86

<Route path="/profile" element={<Profile />} />

87

</Routes>

88

</Router>

89

</QueryClientProvider>

90

);

91

}

92

93

// Multiple contexts for different parts of app

94

const mainQueryClient = new QueryClient();

95

const adminQueryClient = new QueryClient({

96

defaultOptions: {

97

queries: { staleTime: 0 } // Always fresh for admin data

98

}

99

});

100

101

function AppWithMultipleContexts() {

102

return (

103

<QueryClientProvider client={mainQueryClient}>

104

<div>

105

<MainApp />

106

<QueryClientProvider client={adminQueryClient}>

107

<AdminPanel />

108

</QueryClientProvider>

109

</div>

110

</QueryClientProvider>

111

);

112

}

113

```

114

115

### useQueryClient Hook

116

117

Hook to access the QueryClient instance from context.

118

119

```typescript { .api }

120

/**

121

* Access QueryClient instance from React context

122

* @param options - Optional context configuration

123

* @returns QueryClient instance

124

* @throws Error if no QueryClient is found in context

125

*/

126

function useQueryClient(options?: ContextOptions): QueryClient;

127

128

interface ContextOptions {

129

/** Custom React context to read from */

130

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

131

}

132

```

133

134

**Usage Examples:**

135

136

```typescript

137

import { useQueryClient } from "react-query";

138

139

// Basic usage

140

function MyComponent() {

141

const queryClient = useQueryClient();

142

143

const handleInvalidate = () => {

144

queryClient.invalidateQueries({ queryKey: ['posts'] });

145

};

146

147

const handleSetData = (newData: any) => {

148

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

149

};

150

151

return (

152

<div>

153

<button onClick={handleInvalidate}>Refresh Posts</button>

154

<button onClick={() => handleSetData({ name: 'New Name' })}>

155

Update User

156

</button>

157

</div>

158

);

159

}

160

161

// With custom context

162

const AdminContext = React.createContext<QueryClient | undefined>(undefined);

163

164

function AdminComponent() {

165

const adminQueryClient = useQueryClient({ context: AdminContext });

166

167

const clearAdminCache = () => {

168

adminQueryClient.clear();

169

};

170

171

return (

172

<button onClick={clearAdminCache}>Clear Admin Cache</button>

173

);

174

}

175

```

176

177

### Default Context

178

179

The default React context used by React Query.

180

181

```typescript { .api }

182

/**

183

* Default React context for QueryClient

184

* Used automatically when no custom context is provided

185

*/

186

const defaultContext: React.Context<QueryClient | undefined>;

187

```

188

189

### QueryClient Class

190

191

The core client class that manages queries and mutations.

192

193

```typescript { .api }

194

/**

195

* Central client for managing queries and mutations

196

* Handles caching, invalidation, and coordination

197

*/

198

class QueryClient {

199

constructor(options?: QueryClientConfig);

200

201

/** Get cached query data */

202

getQueryData<TData = unknown>(queryKey: QueryKey): TData | undefined;

203

204

/** Set query data in cache */

205

setQueryData<TData>(

206

queryKey: QueryKey,

207

data: TData | ((oldData: TData | undefined) => TData),

208

options?: SetDataOptions

209

): TData | undefined;

210

211

/** Get query state information */

212

getQueryState(queryKey: QueryKey): QueryState | undefined;

213

214

/** Invalidate queries to trigger refetch */

215

invalidateQueries(filters?: InvalidateQueryFilters): Promise<void>;

216

217

/** Refetch queries immediately */

218

refetchQueries(filters?: RefetchQueryFilters): Promise<QueryObserverResult[]>;

219

220

/** Fetch query data imperatively */

221

fetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(

222

options: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>

223

): Promise<TData>;

224

225

/** Fetch query data with separate parameters */

226

fetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(

227

queryKey: TQueryKey,

228

queryFn: QueryFunction<TQueryFnData, TQueryKey>,

229

options?: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>

230

): Promise<TData>;

231

232

/** Prefetch query data without subscribing */

233

prefetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(

234

options: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>

235

): Promise<void>;

236

237

/** Prefetch query data with separate parameters */

238

prefetchQuery<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(

239

queryKey: TQueryKey,

240

queryFn: QueryFunction<TQueryFnData, TQueryKey>,

241

options?: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>

242

): Promise<void>;

243

244

/** Cancel ongoing queries */

245

cancelQueries(filters?: CancelQueryFilters): Promise<void>;

246

247

/** Remove queries from cache */

248

removeQueries(filters?: RemoveQueryFilters): void;

249

250

/** Clear all cached data */

251

clear(): void;

252

253

/** Execute mutation */

254

executeMutation<TData, TError, TVariables, TContext>(

255

options: MutationOptions<TData, TError, TVariables, TContext>

256

): Promise<TData>;

257

258

/** Check if queries are fetching */

259

isFetching(filters?: QueryFilters): number;

260

261

/** Check if mutations are pending */

262

isMutating(filters?: MutationFilters): number;

263

264

/** Get default query options */

265

defaultQueryOptions<T extends QueryOptions>(options?: T): T;

266

267

/** Get default mutation options */

268

defaultMutationOptions<T extends MutationOptions>(options?: T): T;

269

270

/** Mount client (called automatically by provider) */

271

mount(): void;

272

273

/** Unmount client (called automatically by provider) */

274

unmount(): void;

275

}

276

277

interface QueryClientConfig {

278

/** Default options for all queries */

279

queryCache?: QueryCache;

280

/** Default options for all mutations */

281

mutationCache?: MutationCache;

282

/** Default options for queries */

283

defaultOptions?: {

284

queries?: QueryOptions;

285

mutations?: MutationOptions;

286

};

287

/** Custom logger */

288

logger?: Logger;

289

}

290

291

interface FetchQueryOptions<TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey> extends QueryOptions<TQueryFnData, TError, TData, TQueryKey> {

292

queryKey: TQueryKey;

293

queryFn: QueryFunction<TQueryFnData, TQueryKey>;

294

}

295

296

interface InvalidateQueryFilters<TPageData = unknown> extends QueryFilters {

297

refetchType?: 'active' | 'inactive' | 'all' | 'none';

298

}

299

300

interface RefetchQueryFilters<TPageData = unknown> extends QueryFilters {

301

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

302

}

303

304

interface CancelQueryFilters extends QueryFilters {}

305

306

interface RemoveQueryFilters extends QueryFilters {}

307

308

interface SetDataOptions {

309

updatedAt?: number;

310

}

311

```

312

313

## Advanced Usage Patterns

314

315

### Custom Client Configuration

316

317

Creating QueryClient with specific configurations:

318

319

```typescript

320

import { QueryClient, QueryClientProvider } from "react-query";

321

322

// Production configuration

323

const prodQueryClient = new QueryClient({

324

defaultOptions: {

325

queries: {

326

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

327

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

328

retry: (failureCount, error) => {

329

// Don't retry 4xx errors

330

if (error.status >= 400 && error.status < 500) {

331

return false;

332

}

333

return failureCount < 3;

334

},

335

retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000)

336

},

337

mutations: {

338

retry: 1,

339

onError: (error) => {

340

// Global error handling

341

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

342

// Could trigger toast notification, analytics, etc.

343

}

344

}

345

}

346

});

347

348

// Development configuration

349

const devQueryClient = new QueryClient({

350

defaultOptions: {

351

queries: {

352

staleTime: 0, // Always fresh in development

353

cacheTime: 0, // No caching in development

354

retry: false, // Don't retry in development

355

refetchOnWindowFocus: false

356

}

357

},

358

logger: {

359

log: console.log,

360

warn: console.warn,

361

error: console.error

362

}

363

});

364

365

const queryClient = process.env.NODE_ENV === 'production'

366

? prodQueryClient

367

: devQueryClient;

368

```

369

370

### Multiple Client Isolation

371

372

Using separate QueryClients for different application areas:

373

374

```typescript

375

// Separate clients for different data domains

376

const userDataClient = new QueryClient({

377

defaultOptions: {

378

queries: {

379

staleTime: 10 * 60 * 1000, // User data is relatively stable

380

cacheTime: 30 * 60 * 1000

381

}

382

}

383

});

384

385

const realTimeClient = new QueryClient({

386

defaultOptions: {

387

queries: {

388

staleTime: 0, // Real-time data should always be fresh

389

cacheTime: 5 * 60 * 1000,

390

refetchInterval: 30000 // Poll every 30 seconds

391

}

392

}

393

});

394

395

const analyticsClient = new QueryClient({

396

defaultOptions: {

397

queries: {

398

staleTime: 5 * 60 * 1000,

399

cacheTime: 60 * 60 * 1000, // Cache analytics for 1 hour

400

retry: 0 // Don't retry analytics queries

401

}

402

}

403

});

404

405

function App() {

406

return (

407

<QueryClientProvider client={userDataClient}>

408

<div>

409

<UserProfile />

410

411

<QueryClientProvider client={realTimeClient}>

412

<LiveDashboard />

413

</QueryClientProvider>

414

415

<QueryClientProvider client={analyticsClient}>

416

<AnalyticsPanel />

417

</QueryClientProvider>

418

</div>

419

</QueryClientProvider>

420

);

421

}

422

```

423

424

### Manual Cache Management

425

426

Direct cache manipulation using QueryClient:

427

428

```typescript

429

function CacheManager() {

430

const queryClient = useQueryClient();

431

432

const preloadUserData = async (userId: string) => {

433

// Preload user data

434

await queryClient.prefetchQuery({

435

queryKey: ['user', userId],

436

queryFn: () => fetchUser(userId),

437

staleTime: 10 * 60 * 1000

438

});

439

};

440

441

const updateUserInCache = (userId: string, updates: Partial<User>) => {

442

// Update user data in multiple cache locations

443

queryClient.setQueryData(['user', userId], (oldUser: User) => ({

444

...oldUser,

445

...updates

446

}));

447

448

// Update user in lists

449

queryClient.setQueryData(['users'], (oldUsers: User[]) =>

450

oldUsers.map(user =>

451

user.id === userId ? { ...user, ...updates } : user

452

)

453

);

454

};

455

456

const invalidateRelatedData = (userId: string) => {

457

// Invalidate all user-related queries

458

queryClient.invalidateQueries({

459

queryKey: ['user', userId]

460

});

461

462

// Invalidate user posts

463

queryClient.invalidateQueries({

464

queryKey: ['posts'],

465

predicate: (query) => {

466

return query.queryKey.includes(userId);

467

}

468

});

469

};

470

471

const optimisticUpdate = (userId: string, newData: Partial<User>) => {

472

// Store current data for rollback

473

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

474

475

// Apply optimistic update

476

queryClient.setQueryData(['user', userId], (old: User) => ({

477

...old,

478

...newData

479

}));

480

481

return () => {

482

// Rollback function

483

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

484

};

485

};

486

487

return (

488

<div>

489

<button onClick={() => preloadUserData('123')}>

490

Preload User 123

491

</button>

492

<button onClick={() => updateUserInCache('123', { name: 'New Name' })}>

493

Update User 123 Name

494

</button>

495

<button onClick={() => invalidateRelatedData('123')}>

496

Refresh User 123 Data

497

</button>

498

</div>

499

);

500

}

501

```

502

503

### Context Sharing for Microfrontends

504

505

Sharing QueryClient across different React applications:

506

507

```typescript

508

// In microfrontend setup

509

function MicrofrontendApp() {

510

const queryClient = new QueryClient();

511

512

return (

513

<QueryClientProvider

514

client={queryClient}

515

contextSharing={true} // Share context across bundles

516

>

517

<MyMicrofrontendContent />

518

</QueryClientProvider>

519

);

520

}

521

522

// This allows different React Query versions/bundles

523

// to share the same QueryClient instance when rendered

524

// in the same window/document

525

```

526

527

### Error Boundaries Integration

528

529

Integrating QueryClient with React error boundaries:

530

531

```typescript

532

class QueryErrorBoundary extends React.Component {

533

constructor(props) {

534

super(props);

535

this.state = { hasError: false };

536

}

537

538

static getDerivedStateFromError(error) {

539

return { hasError: true };

540

}

541

542

componentDidCatch(error, errorInfo) {

543

// Get QueryClient from context if needed

544

const queryClient = this.context;

545

546

// Clear potentially corrupted cache

547

queryClient?.clear();

548

549

// Log error

550

console.error('Query error boundary caught error:', error, errorInfo);

551

}

552

553

render() {

554

if (this.state.hasError) {

555

return (

556

<div>

557

<h2>Something went wrong.</h2>

558

<button onClick={() => this.setState({ hasError: false })}>

559

Try again

560

</button>

561

</div>

562

);

563

}

564

565

return this.props.children;

566

}

567

}

568

569

function AppWithErrorBoundary() {

570

return (

571

<QueryClientProvider client={queryClient}>

572

<QueryErrorBoundary>

573

<App />

574

</QueryErrorBoundary>

575

</QueryClientProvider>

576

);

577

}

578

```