@apollo/client 4.2.0-alpha.2 → 4.2.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +240 -0
- package/__cjs/cache/core/cache.cjs +1 -1
- package/__cjs/cache/inmemory/entityStore.cjs +3 -3
- package/__cjs/cache/inmemory/entityStore.cjs.map +1 -1
- package/__cjs/cache/inmemory/key-extractor.cjs +1 -1
- package/__cjs/cache/inmemory/policies.cjs +4 -4
- package/__cjs/cache/inmemory/readFromStore.cjs +2 -2
- package/__cjs/cache/inmemory/writeToStore.cjs +4 -4
- package/__cjs/core/ApolloClient.cjs +52 -20
- package/__cjs/core/ApolloClient.cjs.map +1 -1
- package/__cjs/core/ApolloClient.d.cts +159 -22
- package/__cjs/core/ObservableQuery.cjs +7 -7
- package/__cjs/core/ObservableQuery.cjs.map +1 -1
- package/__cjs/core/ObservableQuery.d.cts +20 -1
- package/__cjs/core/QueryManager.cjs +12 -12
- package/__cjs/core/QueryManager.cjs.map +1 -1
- package/__cjs/core/RefetchEventManager.cjs +134 -0
- package/__cjs/core/RefetchEventManager.cjs.map +1 -0
- package/__cjs/core/RefetchEventManager.d.cts +130 -0
- package/__cjs/core/index.cjs +7 -1
- package/__cjs/core/index.cjs.map +1 -1
- package/__cjs/core/index.d.cts +4 -1
- package/__cjs/core/refetchSources/onlineSource.cjs +10 -0
- package/__cjs/core/refetchSources/onlineSource.cjs.map +1 -0
- package/__cjs/core/refetchSources/onlineSource.d.cts +3 -0
- package/__cjs/core/refetchSources/windowFocusSource.cjs +13 -0
- package/__cjs/core/refetchSources/windowFocusSource.cjs.map +1 -0
- package/__cjs/core/refetchSources/windowFocusSource.d.cts +3 -0
- package/__cjs/core/types.d.cts +20 -0
- package/__cjs/invariantErrorCodes.cjs +69 -44
- package/__cjs/link/ws/index.cjs +9 -1
- package/__cjs/link/ws/index.cjs.map +1 -1
- package/__cjs/link/ws/index.d.cts +1 -1
- package/__cjs/react/hooks/useBackgroundQuery.cjs.map +1 -1
- package/__cjs/react/hooks/useBackgroundQuery.d.cts +1486 -66
- package/__cjs/react/hooks/useLazyQuery.cjs +1 -0
- package/__cjs/react/hooks/useLazyQuery.cjs.map +1 -1
- package/__cjs/react/hooks/useLazyQuery.d.cts +366 -40
- package/__cjs/react/hooks/useLoadableQuery.cjs.map +1 -1
- package/__cjs/react/hooks/useLoadableQuery.d.cts +512 -50
- package/__cjs/react/hooks/useMutation.cjs +5 -48
- package/__cjs/react/hooks/useMutation.cjs.map +1 -1
- package/__cjs/react/hooks/useMutation.d.cts +239 -130
- package/__cjs/react/hooks/useQuery.cjs.map +1 -1
- package/__cjs/react/hooks/useQuery.d.cts +590 -41
- package/__cjs/react/hooks/useSubscription.cjs +1 -1
- package/__cjs/react/hooks/useSubscription.cjs.map +1 -1
- package/__cjs/react/hooks/useSubscription.d.cts +2 -2
- package/__cjs/react/hooks/useSuspenseQuery.cjs.map +1 -1
- package/__cjs/react/hooks/useSuspenseQuery.d.cts +754 -46
- package/__cjs/react/internal/cache/QueryReference.cjs +1 -0
- package/__cjs/react/internal/cache/QueryReference.cjs.map +1 -1
- package/__cjs/react/internal/cache/QueryReference.d.cts +1 -1
- package/__cjs/react/query-preloader/createQueryPreloader.cjs.map +1 -1
- package/__cjs/react/query-preloader/createQueryPreloader.d.cts +20 -1
- package/__cjs/react/types/types.documentation.d.cts +19 -0
- package/__cjs/version.cjs +1 -1
- package/cache/core/cache.js +1 -1
- package/cache/inmemory/entityStore.js +3 -3
- package/cache/inmemory/entityStore.js.map +1 -1
- package/cache/inmemory/key-extractor.js +1 -1
- package/cache/inmemory/policies.js +4 -4
- package/cache/inmemory/readFromStore.js +2 -2
- package/cache/inmemory/writeToStore.js +4 -4
- package/core/ApolloClient.d.ts +159 -22
- package/core/ApolloClient.js +53 -21
- package/core/ApolloClient.js.map +1 -1
- package/core/ObservableQuery.d.ts +20 -1
- package/core/ObservableQuery.js +7 -7
- package/core/ObservableQuery.js.map +1 -1
- package/core/QueryManager.js +12 -12
- package/core/QueryManager.js.map +1 -1
- package/core/RefetchEventManager.d.ts +130 -0
- package/core/RefetchEventManager.js +126 -0
- package/core/RefetchEventManager.js.map +1 -0
- package/core/index.d.ts +4 -1
- package/core/index.js +3 -0
- package/core/index.js.map +1 -1
- package/core/refetchSources/onlineSource.d.ts +3 -0
- package/core/refetchSources/onlineSource.js +6 -0
- package/core/refetchSources/onlineSource.js.map +1 -0
- package/core/refetchSources/windowFocusSource.d.ts +3 -0
- package/core/refetchSources/windowFocusSource.js +9 -0
- package/core/refetchSources/windowFocusSource.js.map +1 -0
- package/core/types.d.ts +20 -0
- package/core/types.js.map +1 -1
- package/invariantErrorCodes.js +69 -44
- package/link/ws/index.d.ts +1 -1
- package/link/ws/index.js +9 -1
- package/link/ws/index.js.map +1 -1
- package/package.json +3 -7
- package/react/hooks/useBackgroundQuery.d.ts +1486 -66
- package/react/hooks/useBackgroundQuery.js.map +1 -1
- package/react/hooks/useLazyQuery.d.ts +366 -40
- package/react/hooks/useLazyQuery.js +1 -0
- package/react/hooks/useLazyQuery.js.map +1 -1
- package/react/hooks/useLoadableQuery.d.ts +512 -50
- package/react/hooks/useLoadableQuery.js.map +1 -1
- package/react/hooks/useMutation.d.ts +239 -130
- package/react/hooks/useMutation.js +5 -48
- package/react/hooks/useMutation.js.map +1 -1
- package/react/hooks/useQuery.d.ts +590 -41
- package/react/hooks/useQuery.js.map +1 -1
- package/react/hooks/useSubscription.d.ts +2 -2
- package/react/hooks/useSubscription.js +1 -1
- package/react/hooks/useSubscription.js.map +1 -1
- package/react/hooks/useSuspenseQuery.d.ts +754 -46
- package/react/hooks/useSuspenseQuery.js.map +1 -1
- package/react/hooks-compiled/useBackgroundQuery.d.ts +1486 -66
- package/react/hooks-compiled/useBackgroundQuery.js.map +1 -1
- package/react/hooks-compiled/useLazyQuery.d.ts +366 -40
- package/react/hooks-compiled/useLazyQuery.js +1 -0
- package/react/hooks-compiled/useLazyQuery.js.map +1 -1
- package/react/hooks-compiled/useLoadableQuery.d.ts +512 -50
- package/react/hooks-compiled/useLoadableQuery.js.map +1 -1
- package/react/hooks-compiled/useMutation.d.ts +239 -130
- package/react/hooks-compiled/useMutation.js +4 -47
- package/react/hooks-compiled/useMutation.js.map +1 -1
- package/react/hooks-compiled/useQuery.d.ts +590 -41
- package/react/hooks-compiled/useQuery.js.map +1 -1
- package/react/hooks-compiled/useSubscription.d.ts +2 -2
- package/react/hooks-compiled/useSubscription.js +1 -1
- package/react/hooks-compiled/useSubscription.js.map +1 -1
- package/react/hooks-compiled/useSuspenseQuery.d.ts +754 -46
- package/react/hooks-compiled/useSuspenseQuery.js.map +1 -1
- package/react/internal/cache/QueryReference.d.ts +1 -1
- package/react/internal/cache/QueryReference.js +1 -0
- package/react/internal/cache/QueryReference.js.map +1 -1
- package/react/query-preloader/createQueryPreloader.d.ts +20 -1
- package/react/query-preloader/createQueryPreloader.js.map +1 -1
- package/react/types/types.documentation.d.ts +19 -0
- package/react/types/types.documentation.js.map +1 -1
- package/skills/apollo-client/SKILL.md +168 -0
- package/skills/apollo-client/references/caching.md +560 -0
- package/skills/apollo-client/references/error-handling.md +350 -0
- package/skills/apollo-client/references/fragments.md +804 -0
- package/skills/apollo-client/references/integration-client.md +336 -0
- package/skills/apollo-client/references/integration-nextjs.md +325 -0
- package/skills/apollo-client/references/integration-react-router.md +256 -0
- package/skills/apollo-client/references/integration-tanstack-start.md +378 -0
- package/skills/apollo-client/references/mutations.md +549 -0
- package/skills/apollo-client/references/queries.md +416 -0
- package/skills/apollo-client/references/state-management.md +428 -0
- package/skills/apollo-client/references/suspense-hooks.md +773 -0
- package/skills/apollo-client/references/troubleshooting.md +487 -0
- package/skills/apollo-client/references/typescript-codegen.md +133 -0
- package/version.js +1 -1
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
# Caching Reference
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [InMemoryCache Setup](#inmemorycache-setup)
|
|
6
|
+
- [Cache Normalization](#cache-normalization)
|
|
7
|
+
- [Type Policies](#type-policies)
|
|
8
|
+
- [Field Policies](#field-policies)
|
|
9
|
+
- [Pagination](#pagination)
|
|
10
|
+
- [Cache Manipulation](#cache-manipulation)
|
|
11
|
+
- [Garbage Collection](#garbage-collection)
|
|
12
|
+
|
|
13
|
+
## InMemoryCache Setup
|
|
14
|
+
|
|
15
|
+
### Basic Configuration
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { InMemoryCache } from "@apollo/client";
|
|
19
|
+
|
|
20
|
+
const cache = new InMemoryCache({
|
|
21
|
+
// Custom type policies
|
|
22
|
+
typePolicies: {
|
|
23
|
+
Query: {
|
|
24
|
+
fields: {
|
|
25
|
+
// Query-level field policies
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
User: {
|
|
29
|
+
keyFields: ["id"],
|
|
30
|
+
fields: {
|
|
31
|
+
// User-level field policies
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// Custom type name handling (rare)
|
|
37
|
+
possibleTypes: {
|
|
38
|
+
Character: ["Human", "Droid"],
|
|
39
|
+
Node: ["User", "Post", "Comment"],
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Constructor Options
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
new InMemoryCache({
|
|
48
|
+
// Define how types are identified in cache
|
|
49
|
+
typePolicies: {
|
|
50
|
+
/* ... */
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// Interface/union type mappings between supertypes and their subtypes
|
|
54
|
+
possibleTypes: {
|
|
55
|
+
/* ... */
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
// Custom function to generate cache IDs (rare)
|
|
59
|
+
dataIdFromObject: (object) => {
|
|
60
|
+
if (object.__typename === "Book") {
|
|
61
|
+
return `Book:${object.isbn}`;
|
|
62
|
+
}
|
|
63
|
+
return defaultDataIdFromObject(object);
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Cache Normalization
|
|
69
|
+
|
|
70
|
+
Apollo Client normalizes data by splitting query results into individual objects and storing them by unique identifier.
|
|
71
|
+
|
|
72
|
+
### How Normalization Works
|
|
73
|
+
|
|
74
|
+
```graphql
|
|
75
|
+
# Query
|
|
76
|
+
query GetPost {
|
|
77
|
+
post(id: "1") {
|
|
78
|
+
id
|
|
79
|
+
title
|
|
80
|
+
author {
|
|
81
|
+
id
|
|
82
|
+
name
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// Normalized cache structure
|
|
90
|
+
{
|
|
91
|
+
'Post:1': {
|
|
92
|
+
__typename: 'Post',
|
|
93
|
+
id: '1',
|
|
94
|
+
title: 'Hello World',
|
|
95
|
+
author: { __ref: 'User:42' }
|
|
96
|
+
},
|
|
97
|
+
'User:42': {
|
|
98
|
+
__typename: 'User',
|
|
99
|
+
id: '42',
|
|
100
|
+
name: 'John'
|
|
101
|
+
},
|
|
102
|
+
ROOT_QUERY: {
|
|
103
|
+
'post({"id":"1"})': { __ref: 'Post:1' }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Benefits of Normalization
|
|
109
|
+
|
|
110
|
+
1. **Automatic updates**: When `User:42` is updated anywhere, all components showing that user update
|
|
111
|
+
2. **Deduplication**: Same objects aren't stored multiple times
|
|
112
|
+
3. **Efficient updates**: Only changed objects trigger re-renders
|
|
113
|
+
|
|
114
|
+
## Type Policies
|
|
115
|
+
|
|
116
|
+
### keyFields
|
|
117
|
+
|
|
118
|
+
Customize how objects are identified in the cache.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const cache = new InMemoryCache({
|
|
122
|
+
typePolicies: {
|
|
123
|
+
// Use ISBN instead of id for books
|
|
124
|
+
Book: {
|
|
125
|
+
keyFields: ["isbn"],
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
// Composite key
|
|
129
|
+
UserSession: {
|
|
130
|
+
keyFields: ["userId", "deviceId"],
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
// Nested key
|
|
134
|
+
Review: {
|
|
135
|
+
keyFields: ["book", ["isbn"], "reviewer", ["id"]],
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
// No key fields (singleton, only one object in cache per type)
|
|
139
|
+
AppSettings: {
|
|
140
|
+
keyFields: [],
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// Disable normalization (objects of this type will be stored with their
|
|
144
|
+
// parent entity. The same object might end up multiple times in the cache
|
|
145
|
+
// and run out of sync. Use with caution, only if this object really relates
|
|
146
|
+
// to a property of their parent entity and cannot exist on its own.)
|
|
147
|
+
Address: {
|
|
148
|
+
keyFields: false,
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### merge Functions
|
|
155
|
+
|
|
156
|
+
Control how incoming data merges with existing data.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const cache = new InMemoryCache({
|
|
160
|
+
typePolicies: {
|
|
161
|
+
User: {
|
|
162
|
+
fields: {
|
|
163
|
+
// Deep merge profile object
|
|
164
|
+
profile: {
|
|
165
|
+
merge: true, // Shorthand for deep merge
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
// Custom merge logic
|
|
169
|
+
notifications: {
|
|
170
|
+
merge(existing = [], incoming, { mergeObjects }) {
|
|
171
|
+
// Prepend new notifications
|
|
172
|
+
return [...incoming, ...existing];
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Field Policies
|
|
182
|
+
|
|
183
|
+
### read Function
|
|
184
|
+
|
|
185
|
+
Transform cached data when reading.
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const cache = new InMemoryCache({
|
|
189
|
+
typePolicies: {
|
|
190
|
+
User: {
|
|
191
|
+
fields: {
|
|
192
|
+
// Computed field
|
|
193
|
+
fullName: {
|
|
194
|
+
read(_, { readField }) {
|
|
195
|
+
const firstName = readField("firstName");
|
|
196
|
+
const lastName = readField("lastName");
|
|
197
|
+
return `${firstName} ${lastName}`;
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// Transform existing field
|
|
202
|
+
birthDate: {
|
|
203
|
+
read(existing) {
|
|
204
|
+
return existing ? new Date(existing) : null;
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
// Default value
|
|
209
|
+
role: {
|
|
210
|
+
read(existing = "USER") {
|
|
211
|
+
return existing;
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### merge Function
|
|
221
|
+
|
|
222
|
+
Control how incoming data is stored.
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
const cache = new InMemoryCache({
|
|
226
|
+
typePolicies: {
|
|
227
|
+
User: {
|
|
228
|
+
fields: {
|
|
229
|
+
// Accumulate items instead of replacing
|
|
230
|
+
friends: {
|
|
231
|
+
merge(existing = [], incoming) {
|
|
232
|
+
return [...existing, ...incoming];
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
// Merge objects deeply
|
|
237
|
+
settings: {
|
|
238
|
+
merge(existing, incoming, { mergeObjects }) {
|
|
239
|
+
return mergeObjects(existing, incoming);
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
Query: {
|
|
246
|
+
fields: {
|
|
247
|
+
// Merge paginated results
|
|
248
|
+
posts: {
|
|
249
|
+
keyArgs: ["category"], // Only category affects cache key
|
|
250
|
+
merge(existing = { items: [] }, incoming) {
|
|
251
|
+
return {
|
|
252
|
+
...incoming,
|
|
253
|
+
items: [...existing.items, ...incoming.items],
|
|
254
|
+
};
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### keyArgs
|
|
264
|
+
|
|
265
|
+
Control which arguments affect cache storage.
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
const cache = new InMemoryCache({
|
|
269
|
+
typePolicies: {
|
|
270
|
+
Query: {
|
|
271
|
+
fields: {
|
|
272
|
+
// Different cache entry per userId only
|
|
273
|
+
// (limit, offset don't create new entries)
|
|
274
|
+
userPosts: {
|
|
275
|
+
keyArgs: ["userId"],
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
// No arguments affect cache key
|
|
279
|
+
// (useful for pagination)
|
|
280
|
+
feed: {
|
|
281
|
+
keyArgs: false,
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
// Nested argument
|
|
285
|
+
search: {
|
|
286
|
+
keyArgs: ["filter", ["category", "status"]],
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Pagination
|
|
295
|
+
|
|
296
|
+
### Offset-Based Pagination
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
import { offsetLimitPagination } from "@apollo/client/utilities";
|
|
300
|
+
|
|
301
|
+
const cache = new InMemoryCache({
|
|
302
|
+
typePolicies: {
|
|
303
|
+
Query: {
|
|
304
|
+
fields: {
|
|
305
|
+
posts: offsetLimitPagination(),
|
|
306
|
+
|
|
307
|
+
// With key arguments
|
|
308
|
+
userPosts: offsetLimitPagination(["userId"]),
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Cursor-Based Pagination (Relay Style)
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import { relayStylePagination } from "@apollo/client/utilities";
|
|
319
|
+
|
|
320
|
+
const cache = new InMemoryCache({
|
|
321
|
+
typePolicies: {
|
|
322
|
+
Query: {
|
|
323
|
+
fields: {
|
|
324
|
+
posts: relayStylePagination(),
|
|
325
|
+
|
|
326
|
+
// With key arguments
|
|
327
|
+
userPosts: relayStylePagination(["userId"]),
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Custom Pagination
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
const cache = new InMemoryCache({
|
|
338
|
+
typePolicies: {
|
|
339
|
+
Query: {
|
|
340
|
+
fields: {
|
|
341
|
+
feed: {
|
|
342
|
+
keyArgs: false,
|
|
343
|
+
|
|
344
|
+
merge(existing, incoming, { args }) {
|
|
345
|
+
const merged = existing ? existing.slice(0) : [];
|
|
346
|
+
const offset = args?.offset ?? 0;
|
|
347
|
+
|
|
348
|
+
for (let i = 0; i < incoming.length; i++) {
|
|
349
|
+
merged[offset + i] = incoming[i];
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return merged;
|
|
353
|
+
},
|
|
354
|
+
|
|
355
|
+
read(existing, { args }) {
|
|
356
|
+
const offset = args?.offset ?? 0;
|
|
357
|
+
const limit = args?.limit ?? existing?.length ?? 0;
|
|
358
|
+
return existing?.slice(offset, offset + limit);
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### fetchMore for Pagination
|
|
368
|
+
|
|
369
|
+
```tsx
|
|
370
|
+
function PostList() {
|
|
371
|
+
const { data, fetchMore, loading } = useQuery(GET_POSTS, {
|
|
372
|
+
variables: { offset: 0, limit: 10 },
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const loadMore = () => {
|
|
376
|
+
fetchMore({
|
|
377
|
+
variables: {
|
|
378
|
+
offset: data.posts.length,
|
|
379
|
+
},
|
|
380
|
+
// With proper type policies, no updateQuery needed
|
|
381
|
+
});
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
<div>
|
|
386
|
+
{data?.posts.map((post) => <PostCard key={post.id} post={post} />)}
|
|
387
|
+
<button onClick={loadMore} disabled={loading}>
|
|
388
|
+
Load More
|
|
389
|
+
</button>
|
|
390
|
+
</div>
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## Cache Manipulation
|
|
396
|
+
|
|
397
|
+
### cache.readQuery
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
// Read data from cache
|
|
401
|
+
const data = cache.readQuery({
|
|
402
|
+
query: GET_TODOS,
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// With variables
|
|
406
|
+
const userData = cache.readQuery({
|
|
407
|
+
query: GET_USER,
|
|
408
|
+
variables: { id: "1" },
|
|
409
|
+
});
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### cache.writeQuery
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
// Write data to cache
|
|
416
|
+
cache.writeQuery({
|
|
417
|
+
query: GET_TODOS,
|
|
418
|
+
data: {
|
|
419
|
+
todos: [
|
|
420
|
+
{ __typename: "Todo", id: "1", text: "Buy milk", completed: false },
|
|
421
|
+
],
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// With variables
|
|
426
|
+
cache.writeQuery({
|
|
427
|
+
query: GET_USER,
|
|
428
|
+
variables: { id: "1" },
|
|
429
|
+
data: {
|
|
430
|
+
user: { __typename: "User", id: "1", name: "John" },
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### cache.readFragment / cache.writeFragment
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
// Read a specific object - use cache.identify for safety
|
|
439
|
+
const user = cache.readFragment({
|
|
440
|
+
id: cache.identify({ __typename: "User", id: "1" }),
|
|
441
|
+
fragment: gql`
|
|
442
|
+
fragment UserFragment on User {
|
|
443
|
+
id
|
|
444
|
+
name
|
|
445
|
+
email
|
|
446
|
+
}
|
|
447
|
+
`,
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Apollo Client 4.1+: Use 'from' parameter (recommended)
|
|
451
|
+
const user = cache.readFragment({
|
|
452
|
+
from: { __typename: "User", id: "1" },
|
|
453
|
+
fragment: gql`
|
|
454
|
+
fragment UserFragment on User {
|
|
455
|
+
id
|
|
456
|
+
name
|
|
457
|
+
email
|
|
458
|
+
}
|
|
459
|
+
`,
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// Update a specific object
|
|
463
|
+
cache.writeFragment({
|
|
464
|
+
id: cache.identify({ __typename: "User", id: "1" }),
|
|
465
|
+
fragment: gql`
|
|
466
|
+
fragment UpdateUser on User {
|
|
467
|
+
name
|
|
468
|
+
}
|
|
469
|
+
`,
|
|
470
|
+
data: {
|
|
471
|
+
name: "Jane",
|
|
472
|
+
},
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// Apollo Client 4.1+: Use 'from' parameter (recommended)
|
|
476
|
+
cache.writeFragment({
|
|
477
|
+
from: { __typename: "User", id: "1" },
|
|
478
|
+
fragment: gql`
|
|
479
|
+
fragment UpdateUser on User {
|
|
480
|
+
name
|
|
481
|
+
}
|
|
482
|
+
`,
|
|
483
|
+
data: {
|
|
484
|
+
name: "Jane",
|
|
485
|
+
},
|
|
486
|
+
});
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### cache.modify
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
// Modify fields directly
|
|
493
|
+
cache.modify({
|
|
494
|
+
id: cache.identify(user),
|
|
495
|
+
fields: {
|
|
496
|
+
// Set new value
|
|
497
|
+
name: () => "New Name",
|
|
498
|
+
|
|
499
|
+
// Transform existing value
|
|
500
|
+
postCount: (existing) => existing + 1,
|
|
501
|
+
|
|
502
|
+
// Delete field
|
|
503
|
+
temporaryField: (_, { DELETE }) => DELETE,
|
|
504
|
+
|
|
505
|
+
// Add to array
|
|
506
|
+
friends: (existing, { toReference }) => [
|
|
507
|
+
...existing,
|
|
508
|
+
toReference({ __typename: "User", id: "2" }),
|
|
509
|
+
],
|
|
510
|
+
},
|
|
511
|
+
});
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### cache.evict
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
// Remove object from cache
|
|
518
|
+
cache.evict({ id: "User:1" });
|
|
519
|
+
|
|
520
|
+
// Remove specific field
|
|
521
|
+
cache.evict({ id: "User:1", fieldName: "friends" });
|
|
522
|
+
|
|
523
|
+
// Remove with broadcast (trigger re-renders)
|
|
524
|
+
cache.evict({ id: "User:1", broadcast: true });
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
## Garbage Collection
|
|
528
|
+
|
|
529
|
+
### Manual Garbage Collection
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
// After evicting objects, clean up dangling references
|
|
533
|
+
cache.evict({ id: "User:1" });
|
|
534
|
+
cache.gc();
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
### Retaining Objects
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
// Prevent objects from being garbage collected
|
|
541
|
+
const release = cache.retain("User:1");
|
|
542
|
+
|
|
543
|
+
// Later, allow GC
|
|
544
|
+
release();
|
|
545
|
+
cache.gc();
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### Inspecting Cache
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
// Get all cached data
|
|
552
|
+
const cacheContents = cache.extract();
|
|
553
|
+
|
|
554
|
+
// Restore cache state
|
|
555
|
+
cache.restore(previousCacheContents);
|
|
556
|
+
|
|
557
|
+
// Get identified object cache key
|
|
558
|
+
const userId = cache.identify({ __typename: "User", id: "1" });
|
|
559
|
+
// Returns: 'User:1'
|
|
560
|
+
```
|