ferry
ferry copied to clipboard
Cache not updated after adding paginated data through updateResult with ferry
I have a paginated list of photos (by groups of 50) that I fetch via a graphql query through ferry:
query Photos(
$paging: CursorPaging!
) {
photos(paging: $paging) {
edges {
cursor
node {
id
name
}
}
pageInfo {
startCursor
endCursor
hasNextPage
}
totalCount
}
}
I've set a requestID such as 'AllMyPhotos' not dependant on the cursor and have checked that that requestID is used everywhere.
When I want to load more data, I use the updateResult function:
fetchMore: (_) {
if (data == null) return;
final endCursor = data!.photos.pageInfo.endCursor?.value;
if (endCursor == null) return;
final client = locator<Client>();
final request = ref.read(photosReqProvider);
client.requestController.add(request.rebuild((req) {
req.vars.paging.after.value = endCursor;
req.updateResult = combinePhotos;
}));
},
GPhotosData? combinePhotos(GPhotosData? prev, GPhotosData? next) {
if (prev == null) return next;
return next?.rebuild((data) {
data.photos.edges.insertAll(0, prev.photos.edges);
});
}
I manage to get all the data in data.photos
correctly.
But when I read the cache I only have the first 50 items inside and not all the items I have loaded so far.
final cache = locator<Client>().cache;
final photosData = cache.readQuery(photosReq);
if (photosData == null) return;
final photoIDs = photosData.projectPhotos.edges.map((edge) => edge.node.id).toList();
// photosIDs is always 50, even when the data previously contained 100,150, etc when fetchingMore
Does updateResult
not update the cache for that requestID ?
Looking at the code, I think the issue comes from the FetchPolicyTypedLink
that calls _writeToCache
/cache.writeQuery
which calls normalizeOperation
but never passes the requestId
.
@knaeckeKami do you think it'd be ok to pass the requestId
in the data of resolveDataId
? Then we could use the typePolicies
to group the requests together.
The cache will cache all queries, but it will cache them together with the arguments for paging, and if you read only the first page, it will only return the first page.
But you can add configuration to merge all pages together, like described here:
https://ferrygraphql.com/docs/cache-configuration#the-fields-property
In my use case, I have a table view and i can go to the first page, next page, previous page and last page, also I can apply filters and sorting.
So based on this documentation, I should be setting my ['filter', 'sorting'] as keyArgs as I do not want results of queries with different filters/sorting to be merged together.
How to handle the fact that the user may navigate to the first page, then the last page, then press previous etc. All the results will be added in some random order to my cache ?
Also I will need to not only merge the edges together in my result but also compute the pageInfo to get the startCursor, endCursor and hasNextPage.
query ProjectPhotos(
$filter: ProjectPhotoFilter!
$paging: CursorPaging!
$sorting: [ProjectPhotoSort!]
) {
projectPhotos(filter: $filter, paging: $paging, sorting: $sorting) {
edges {
node {
id
name
}
}
pageInfo {
startCursor
endCursor
hasNextPage
}
totalCount
}
}
'Query': TypePolicy(
fields: {
'projectPhotos': FieldPolicy(
keyArgs: const ['filter', 'sorting'],
merge: (existing, incoming, option) {
if (existing == null) return incoming;
final existingEdges = (existing['edges'] as List<dynamic>?) ?? [];
final incomingEdges = (incoming['edges'] as List<dynamic>?) ?? [];
final mergedEdges = (LinkedHashSet<dynamic>(equals: jsonMapEquals, hashCode: const DeepCollectionEquality().hash)
..addAll(existingEdges)
..addAll(incomingEdges))
.toList();
return incoming['edges'] = mergedEdges;
},
),
},
),
For this, you will have to use the 'read' parameter on the FieldPolicy in order to execute your query based on the variables passed to your query.