or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cache-management.mdcore-data-fetching.mdglobal-configuration.mdimmutable-data.mdindex.mdinfinite-loading.mdmutations.mdsubscriptions.md

cache-management.mddocs/

0

# Cache Management

1

2

SWR provides powerful cache management capabilities through global mutate functions and preloading utilities for programmatic cache manipulation and data optimization.

3

4

## Capabilities

5

6

### Global Mutate Function

7

8

Global function for programmatic cache manipulation across all SWR instances.

9

10

```typescript { .api }

11

/**

12

* Global mutate function for cache manipulation

13

* @param key - Key to mutate, or function to match multiple keys

14

* @param data - New data, Promise, or function returning new data

15

* @param options - Mutation options or boolean for revalidate

16

* @returns Promise resolving to the updated data

17

*/

18

function mutate<Data = any>(

19

key: Key | ((key: Key) => boolean),

20

data?: Data | Promise<Data> | MutatorCallback<Data>,

21

options?: boolean | MutatorOptions<Data>

22

): Promise<Data | undefined>;

23

24

// Overload for mutating without providing data (triggers revalidation)

25

function mutate(

26

key: Key | ((key: Key) => boolean),

27

options?: boolean | MutatorOptions

28

): Promise<any>;

29

```

30

31

**Usage Examples:**

32

33

```typescript

34

import { mutate } from "swr";

35

36

// Update specific cache entry

37

await mutate("/api/user", newUserData);

38

39

// Revalidate specific key

40

await mutate("/api/user");

41

42

// Update multiple related keys

43

await mutate((key) => typeof key === "string" && key.startsWith("/api/users"));

44

45

// Optimistic update with rollback

46

await mutate(

47

"/api/user",

48

updateUserAPI(newData),

49

{

50

optimisticData: { ...currentData, ...newData },

51

rollbackOnError: true

52

}

53

);

54

```

55

56

### Preload Function

57

58

Function to preload data into cache before it's needed by components.

59

60

```typescript { .api }

61

/**

62

* Preload data into cache before components need it

63

* @param key - Cache key for the data

64

* @param fetcher - Function to fetch the data

65

* @returns The fetched data

66

*/

67

function preload<Data = any>(

68

key: Key,

69

fetcher: Fetcher<Data, Key>

70

): Data;

71

```

72

73

**Usage Examples:**

74

75

```typescript

76

import { preload } from "swr";

77

78

// Preload critical data on app start

79

preload("/api/user", fetcher);

80

preload("/api/config", fetcher);

81

82

// Preload on hover (before navigation)

83

const handleLinkHover = () => {

84

preload("/api/dashboard", fetcher);

85

};

86

87

// Preload related data after successful mutation

88

const handleUserUpdate = async (userData) => {

89

await mutate("/api/user", userData);

90

91

// Preload related data that user might need next

92

preload("/api/user/preferences", fetcher);

93

preload("/api/user/activity", fetcher);

94

};

95

```

96

97

### Cache Inspection and Management

98

99

Access and manipulate the cache directly for advanced use cases.

100

101

```typescript

102

import { useSWRConfig } from "swr";

103

104

function CacheManager() {

105

const { cache, mutate } = useSWRConfig();

106

107

// Inspect cache contents

108

const logCache = () => {

109

console.log("Cache contents:", cache);

110

111

// Get all cached keys

112

const keys = Array.from(cache.keys());

113

console.log("Cached keys:", keys);

114

115

// Get specific cache entry

116

const userData = cache.get("/api/user");

117

console.log("User data:", userData);

118

};

119

120

// Clear entire cache

121

const clearCache = () => {

122

cache.clear();

123

};

124

125

// Remove specific cache entry

126

const removeCacheEntry = (key: string) => {

127

cache.delete(key);

128

};

129

130

// Batch cache operations

131

const batchCacheUpdate = () => {

132

const updates = [

133

["/api/user", newUserData],

134

["/api/settings", newSettings],

135

["/api/preferences", newPreferences]

136

];

137

138

updates.forEach(([key, data]) => {

139

mutate(key, data, false); // Update without revalidation

140

});

141

};

142

143

return (

144

<div>

145

<button onClick={logCache}>Log Cache</button>

146

<button onClick={clearCache}>Clear Cache</button>

147

<button onClick={batchCacheUpdate}>Batch Update</button>

148

</div>

149

);

150

}

151

```

152

153

### Advanced Mutation Patterns

154

155

**Pattern Matching for Bulk Updates:**

156

157

```typescript

158

// Update all user-related cache entries

159

await mutate(

160

(key) => typeof key === "string" && key.includes("/api/user"),

161

undefined, // No data provided, will revalidate

162

{ revalidate: true }

163

);

164

165

// Update cache entries matching specific pattern

166

await mutate(

167

(key) => {

168

if (typeof key === "string") {

169

return key.startsWith("/api/posts") || key.startsWith("/api/comments");

170

}

171

return false;

172

}

173

);

174

175

// Complex key matching with arrays

176

await mutate(

177

(key) => {

178

if (Array.isArray(key)) {

179

return key[0] === "/api/search" && key[1] === currentQuery;

180

}

181

return false;

182

}

183

);

184

```

185

186

**Conditional Cache Updates:**

187

188

```typescript

189

// Update cache only if data has changed

190

const updateUserIfChanged = async (newUserData: User) => {

191

await mutate(

192

"/api/user",

193

newUserData,

194

{

195

populateCache: (result, currentData) => {

196

// Only update if data actually changed

197

if (JSON.stringify(result) !== JSON.stringify(currentData)) {

198

return result;

199

}

200

return currentData; // Keep existing data

201

},

202

revalidate: false

203

}

204

);

205

};

206

207

// Conditional optimistic updates

208

const optimisticUpdate = async (action: () => Promise<any>) => {

209

const shouldBeOptimistic = checkNetworkCondition();

210

211

if (shouldBeOptimistic) {

212

await mutate(

213

"/api/data",

214

action(),

215

{

216

optimisticData: (current) => ({ ...current, updating: true }),

217

rollbackOnError: true

218

}

219

);

220

} else {

221

// Just do the action without optimistic update

222

await action();

223

await mutate("/api/data"); // Revalidate after

224

}

225

};

226

```

227

228

### Cache Warming Strategies

229

230

**Application Startup:**

231

232

```typescript

233

// Warm cache with essential data on app load

234

const warmCache = async () => {

235

const essentialData = [

236

["/api/user", userFetcher],

237

["/api/config", configFetcher],

238

["/api/permissions", permissionsFetcher]

239

];

240

241

// Preload all essential data in parallel

242

await Promise.all(

243

essentialData.map(([key, fetcher]) => preload(key, fetcher))

244

);

245

};

246

247

// Call during app initialization

248

warmCache();

249

```

250

251

**Predictive Loading:**

252

253

```typescript

254

// Preload data based on user behavior

255

const handleUserInteraction = (action: string) => {

256

switch (action) {

257

case "hover_profile":

258

preload("/api/user/profile", fetcher);

259

preload("/api/user/activity", fetcher);

260

break;

261

262

case "hover_dashboard":

263

preload("/api/dashboard/stats", fetcher);

264

preload("/api/dashboard/notifications", fetcher);

265

break;

266

267

case "search_focus":

268

preload("/api/search/recent", fetcher);

269

preload("/api/search/suggestions", fetcher);

270

break;

271

}

272

};

273

274

// Usage in components

275

<button

276

onMouseEnter={() => handleUserInteraction("hover_dashboard")}

277

onClick={navigateToDashboard}

278

>

279

Dashboard

280

</button>

281

```

282

283

**Route-Based Preloading:**

284

285

```typescript

286

// Preload data for upcoming routes

287

const preloadRoute = (routeName: string) => {

288

const routePreloads = {

289

dashboard: [

290

"/api/dashboard/stats",

291

"/api/dashboard/recent-activity"

292

],

293

profile: [

294

"/api/user/profile",

295

"/api/user/settings"

296

],

297

reports: [

298

"/api/reports/list",

299

"/api/reports/filters"

300

]

301

};

302

303

const keys = routePreloads[routeName] || [];

304

keys.forEach(key => preload(key, fetcher));

305

};

306

307

// Integration with router

308

const router = useRouter();

309

310

useEffect(() => {

311

const handleRouteChangeStart = (url: string) => {

312

const routeName = url.split("/")[1];

313

preloadRoute(routeName);

314

};

315

316

router.events.on("routeChangeStart", handleRouteChangeStart);

317

return () => router.events.off("routeChangeStart", handleRouteChangeStart);

318

}, [router]);

319

```

320

321

### Cache Invalidation Strategies

322

323

**Time-Based Invalidation:**

324

325

```typescript

326

// Invalidate cache entries older than threshold

327

const invalidateStaleCache = () => {

328

const STALE_TIME = 5 * 60 * 1000; // 5 minutes

329

const now = Date.now();

330

331

mutate((key) => {

332

// Get cache entry to check timestamp

333

const entry = cache.get(key);

334

if (entry && entry.timestamp) {

335

return now - entry.timestamp > STALE_TIME;

336

}

337

return false;

338

});

339

};

340

341

// Run periodically

342

setInterval(invalidateStaleCache, 60000); // Every minute

343

```

344

345

**Event-Based Invalidation:**

346

347

```typescript

348

// Invalidate cache based on external events

349

const handleExternalEvent = (event: ExternalEvent) => {

350

switch (event.type) {

351

case "user_updated":

352

// Invalidate all user-related data

353

mutate((key) => typeof key === "string" && key.includes("/api/user"));

354

break;

355

356

case "permissions_changed":

357

// Invalidate permission-related data

358

mutate("/api/permissions");

359

mutate("/api/user/permissions");

360

break;

361

362

case "global_config_changed":

363

// Invalidate configuration data

364

mutate((key) => typeof key === "string" && key.includes("/api/config"));

365

break;

366

}

367

};

368

369

// Listen for external events (WebSocket, SSE, etc.)

370

websocket.on("event", handleExternalEvent);

371

```

372

373

**Smart Invalidation:**

374

375

```typescript

376

// Intelligent cache invalidation based on data relationships

377

const smartInvalidate = async (changedData: any, context: string) => {

378

const invalidationMap = {

379

user_profile: [

380

"/api/user",

381

"/api/user/profile",

382

(key) => typeof key === "string" && key.startsWith("/api/user/")

383

],

384

user_settings: [

385

"/api/user/settings",

386

"/api/user/preferences"

387

],

388

post_created: [

389

"/api/posts",

390

(key) => Array.isArray(key) && key[0] === "/api/posts" && key[1] === "list"

391

]

392

};

393

394

const toInvalidate = invalidationMap[context] || [];

395

396

for (const target of toInvalidate) {

397

if (typeof target === "function") {

398

await mutate(target);

399

} else {

400

await mutate(target);

401

}

402

}

403

};

404

```

405

406

### Performance Optimization

407

408

**Batch Cache Operations:**

409

410

```typescript

411

// Batch multiple cache operations for better performance

412

const batchCacheUpdates = async (updates: Array<[string, any]>) => {

413

// Disable revalidation for all updates except the last one

414

const promises = updates.map(([key, data], index) =>

415

mutate(key, data, { revalidate: index === updates.length - 1 })

416

);

417

418

await Promise.all(promises);

419

};

420

421

// Usage

422

await batchCacheUpdates([

423

["/api/user", newUserData],

424

["/api/settings", newSettings],

425

["/api/preferences", newPreferences]

426

]);

427

```

428

429

**Memory Management:**

430

431

```typescript

432

// Clean up cache to prevent memory leaks

433

const cleanupCache = () => {

434

const { cache } = useSWRConfig();

435

const MAX_CACHE_SIZE = 100;

436

437

if (cache.size > MAX_CACHE_SIZE) {

438

// Remove oldest entries (this is a simplified example)

439

const entries = Array.from(cache.entries());

440

const oldestEntries = entries

441

.sort((a, b) => (a[1].timestamp || 0) - (b[1].timestamp || 0))

442

.slice(0, entries.length - MAX_CACHE_SIZE);

443

444

oldestEntries.forEach(([key]) => cache.delete(key));

445

}

446

};

447

448

// Run cleanup periodically

449

useEffect(() => {

450

const interval = setInterval(cleanupCache, 60000); // Every minute

451

return () => clearInterval(interval);

452

}, []);

453

```

454

455

**Cache Debugging:**

456

457

```typescript

458

// Debugging utilities for cache management

459

const useCacheDebugger = () => {

460

const { cache, mutate } = useSWRConfig();

461

462

const debugCache = {

463

log: () => {

464

console.group("SWR Cache Debug");

465

console.log("Cache size:", cache.size);

466

console.log("Keys:", Array.from(cache.keys()));

467

console.groupEnd();

468

},

469

470

logKey: (key: string) => {

471

const entry = cache.get(key);

472

console.log(`Cache entry for ${key}:`, entry);

473

},

474

475

stats: () => {

476

const keys = Array.from(cache.keys());

477

const typeStats = keys.reduce((acc, key) => {

478

const type = typeof key === "string" ? "string" :

479

Array.isArray(key) ? "array" : "object";

480

acc[type] = (acc[type] || 0) + 1;

481

return acc;

482

}, {} as Record<string, number>);

483

484

console.log("Cache statistics:", typeStats);

485

},

486

487

export: () => {

488

const cacheData = {};

489

cache.forEach((value, key) => {

490

cacheData[JSON.stringify(key)] = value;

491

});

492

return cacheData;

493

}

494

};

495

496

return debugCache;

497

};

498

499

// Usage in development

500

const debugCache = useCacheDebugger();

501

debugCache.log(); // Log current cache state

502

```