Skip to content
This repository was archived by the owner on Apr 17, 2023. It is now read-only.

Commit 124d12b

Browse files
Cache pagination (#524)
1 parent 5399d59 commit 124d12b

File tree

3 files changed

+98
-38
lines changed

3 files changed

+98
-38
lines changed

docs/ref-cache.md

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,30 @@ const options = createMutationOptions(mutationOptions);
7676
apolloClient.mutate(options);
7777
```
7878

79-
> NOTE: Cache helpers currently support only GraphQL Queries that return arrays.
80-
> For example `getUsers():[User]`.
81-
> When working with single objects returned by Queries we usually do not need use any helper as Query will be updated automatically on every update
79+
### Pagination and relationships
80+
81+
Offix by default assumes that the object returned by mutations contains just your data. In situations when you need to wrap your data into some container to provide pagination and other information, Offix will require an additional parameter. For example when the API returns TaskPage etc. the actual data might be returned over `items`.
82+
83+
When this query is cached, it may be necessary to update a field within the query and not the entire query, for example with relationships. In order to do this, it is necessary to provide the name of the field being updated with the `returnField` parameter.
84+
85+
```javascript
86+
const mutationOptions = {
87+
mutation: ADD_COMMENT,
88+
variables: {
89+
title: 'comment title'
90+
},
91+
updateQuery: {
92+
query: GET_TASK,
93+
variables: {
94+
filterBy: 'some filter'
95+
}
96+
},
97+
returnType: 'Comment',
98+
operationType: CacheOperation.ADD,
99+
idField: 'id',
100+
returnField: 'comments
101+
};
102+
```
82103
83104
## Subscription Helpers
84105
@@ -121,6 +142,20 @@ query.subscribeToMore(subscriptionOptions);
121142
122143
The cache will now be kept up to date with automatic data deduplication being performed.
123144
145+
### Pagination and relationships
146+
147+
Similarly to the mutation cache update helpers, it is necessary to provide the `returnField` parameter to specify the name of the field to update within the query.
148+
149+
```javascript
150+
export const editSubscriptionOptions = createSubscriptionOptions({
151+
subscriptionQuery: NEW_COMMENT,
152+
cacheUpdateQuery: GET_TASK,
153+
operationType: CacheOperation.ADD,
154+
idField: 'id',
155+
returnField: 'comments'
156+
});
157+
```
158+
124159
### Multiple Subscriptions
125160
126161
`offix-cache` also provides the ability to automatically call `subscribeToMore` on your `ObservableQuery`. This can be useful in a situation where you may have multiple subscriptions which can affect one single query. For example, if you have a `TaskAdded`, `TaskDeleted` and a `TaskUpdated` subscription you would need three separate `subscribeToMore` function calls. This can become tedious as your number of subscriptions grow. To combat this, we can use the `subscribeToMoreHelper` function from offix-cache to automatically handle this for us by passing it an array of subscriptions and their corresponding queries which need to be updated.

packages/offix-cache/src/createMutationOptions.ts

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ interface CacheUpdateHelperOptions {
6969
* @default uses `id` field on the type
7070
*/
7171
idField?: string;
72+
73+
/**
74+
* String value for possible nested return type, primarily used
75+
* with pagination or relationships.
76+
*
77+
* @default uses null
78+
*/
79+
returnField?: string | null;
7280
}
7381

7482
/**
@@ -173,7 +181,7 @@ export const getUpdateFunction = (options: CacheUpdateOptions): MutationUpdaterF
173181
* Generic cache update function that adds an item to a query that contains a list of items
174182
* Might be exported in the future
175183
*/
176-
function addItemToQuery({ mutationName, updateQuery, idField = "id" }: CacheUpdateHelperOptions): MutationUpdaterFn {
184+
function addItemToQuery({ mutationName, updateQuery, idField = "id", returnField = null }: CacheUpdateHelperOptions): MutationUpdaterFn {
177185
return (cache, { data }) => {
178186
const { query, variables } = deconstructQuery(updateQuery);
179187
const queryField = getOperationFieldName(query);
@@ -186,29 +194,29 @@ function addItemToQuery({ mutationName, updateQuery, idField = "id" }: CacheUpda
186194
queryResult = {};
187195
}
188196

189-
const result = queryResult[queryField];
197+
const result = (returnField)
198+
? queryResult[queryField][returnField]
199+
: queryResult[queryField];
200+
190201
if (result && operationData) {
191202
// FIXME deduplication should happen on subscriptions
192203
// We do that every time no matter if we have subscription
193-
if (result.find) {
204+
if (result instanceof Array) {
194205
const foundItem = !result.find((item: any) => {
195206
return item[idField] === operationData[idField];
196207
});
197208
if (foundItem) {
198209
result.push(operationData);
199210
}
200-
201-
} else if (result.items && result.items.find) {
202-
const foundItem = !result.items.find((item: any) => {
203-
return item[idField] === operationData[idField];
204-
});
205-
if (foundItem) {
206-
result.items.push(operationData);
207-
}
208211
}
209212
} else {
210-
queryResult[queryField] = [operationData];
213+
if (!returnField) {
214+
queryResult[queryField] = [operationData];
215+
} else {
216+
queryResult[queryField][returnField] = [operationData];
217+
}
211218
}
219+
212220
try {
213221
cache.writeQuery({
214222
query,
@@ -226,7 +234,7 @@ function addItemToQuery({ mutationName, updateQuery, idField = "id" }: CacheUpda
226234
* Generic cache update function that removes an item from a query that contains a list of items
227235
* Might be exported in the future
228236
*/
229-
function deleteItemFromQuery({ mutationName, updateQuery, idField = "id" }: CacheUpdateHelperOptions): MutationUpdaterFn {
237+
function deleteItemFromQuery({ mutationName, updateQuery, idField = "id", returnField = null }: CacheUpdateHelperOptions): MutationUpdaterFn {
230238
return (cache, { data }) => {
231239
const { query, variables } = deconstructQuery(updateQuery);
232240
const queryField = getOperationFieldName(query);
@@ -242,18 +250,21 @@ function deleteItemFromQuery({ mutationName, updateQuery, idField = "id" }: Cach
242250
toBeRemoved = operationData;
243251
}
244252
let newData: any;
245-
if (typeof queryResult[queryField].filter === "function") {
246-
newData = queryResult[queryField].filter((item: any) => {
247-
return toBeRemoved[idField] !== item[idField];
248-
});
249-
} else if (queryResult[queryField].items) {
250-
const newItems = queryResult[queryField].items.filter((item: any) => {
253+
254+
const prev = (returnField)
255+
? queryResult[queryField][returnField]
256+
: queryResult[queryField];
257+
258+
if (prev instanceof Array) {
259+
newData = prev.filter((item: any) => {
251260
return toBeRemoved[idField] !== item[idField];
252261
});
253-
newData = queryResult[queryField];
254-
newData.items = newItems;
255262
} else {
256-
newData = queryResult[queryField];
263+
if (!returnField) {
264+
newData = queryResult[queryField];
265+
} else {
266+
newData = queryResult[queryField][returnField];
267+
}
257268
}
258269

259270
queryResult[queryField] = newData;

packages/offix-cache/src/createSubscriptionOptions.ts

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface SubscriptionHelperOptions {
1010
cacheUpdateQuery: CacheUpdatesQuery;
1111
operationType: CacheOperation;
1212
idField?: string;
13+
returnField?: string;
1314
}
1415

1516
/**
@@ -34,7 +35,8 @@ export const createSubscriptionOptions = (options: SubscriptionHelperOptions): S
3435
subscriptionQuery,
3536
cacheUpdateQuery,
3637
operationType,
37-
idField = "id"
38+
idField = "id",
39+
returnField = null
3840
} = options;
3941
const document = (subscriptionQuery && (subscriptionQuery as QueryWithVariables).query)
4042
|| (subscriptionQuery as DocumentNode);
@@ -53,12 +55,27 @@ export const createSubscriptionOptions = (options: SubscriptionHelperOptions): S
5355
const mutadedItem = data[key];
5456

5557
const optype = operationType;
56-
const obj = prev[queryField];
58+
59+
// necessary for relationships
60+
// i.e. comments on a task field
61+
const obj = (returnField)
62+
? prev[queryField][returnField]
63+
: prev[queryField];
5764

5865
const updater = getUpdateQueryFunction(optype, idField);
5966
const result = updater(obj, mutadedItem);
67+
68+
if (!returnField) {
69+
return {
70+
[queryField]: result
71+
};
72+
}
73+
6074
return {
61-
[queryField]: result
75+
[queryField]: {
76+
...prev[queryField],
77+
[returnField]: result
78+
}
6279
};
6380
}
6481
};
@@ -91,12 +108,11 @@ const getUpdateQueryFunction = (operationType: CacheOperation, idField = "id"):
91108
function addSubscriptionItem({ idField }: { idField: string }) {
92109
return (prev: [CacheItem], newItem: CacheItem | undefined) => {
93110
if (!newItem) {
94-
return [...prev];
95-
} else {
96-
return [...prev.filter(item => {
97-
return item[idField] !== newItem[idField];
98-
}), newItem];
111+
return prev;
99112
}
113+
return [...prev.filter(item => {
114+
return item[idField] !== newItem[idField];
115+
}), newItem];
100116
};
101117
};
102118

@@ -108,9 +124,8 @@ function deleteSubscriptionItem({ idField }: { idField: string }) {
108124
return (prev: [CacheItem], newItem: CacheItem | undefined) => {
109125
if (!newItem) {
110126
return [];
111-
} else {
112-
return prev.filter((item: any) => item[idField] !== newItem[idField]);
113127
}
128+
return prev.filter((item: any) => item[idField] !== newItem[idField]);
114129
};
115130
};
116131

@@ -121,9 +136,8 @@ function deleteSubscriptionItem({ idField }: { idField: string }) {
121136
function updateSubscriptionItem({ idField }: { idField: string }) {
122137
return (prev: [CacheItem], newItem: CacheItem | undefined) => {
123138
if (!newItem) {
124-
return [...prev];
125-
} else {
126-
return prev.map((item: any) => item[idField] === newItem[idField] ? newItem : item);
139+
return prev;
127140
}
141+
return prev.map((item: any) => item[idField] === newItem[idField] ? newItem : item);
128142
};
129143
};

0 commit comments

Comments
 (0)