I'm new to Apollo Client and I'm trying to wrap my head around field policies to implement pagination.
So basically I have a category page where I perform a query that is based on the the slug that I receive from the URL of the page, returns a list of IDs (and I pass them down as props for the product component), for example:
query getProductId($slug: String!) {
slug(where: {slug: $slug}){
products {
Id
}
}
}
from this query I get and array of all the objects containing the IDs of the products.
I can pass a "first: " and "after: {id: }" to the products field and this way I could decide after which product ID I want to query. for example:
query getProductId($slug: String!) {
slug(where: {slug: $slug}){
products(first: 4, after: {id: 19}) {
Id
}
}
}
I know that in my ApolloClient instance I can define a field policy for the cache like this:
const apollo = new ApolloClient({
//...
cache: new InMemoryClient({
typePolicies: {
Query: {
fields: {
products: offsetLimitPagination(["<* keyArgs>"]),
},
},
},
})
})
This is just one random helper function I took, but in my case I think using a cursor based strategy is better since I could use the last ID in the list as cursor, I guess(?)
From here I'm completely lost, the more I read the docs the more I get confused.
{
keyArgs: ["first"],
merge(existing, incoming, { args: { cursor }, readField }) {
const merged = existing ? existing.slice(0) : [];
let offset = offsetFromCursor(merged, cursor, readField);
// If we couldn't find the cursor, default to appending to
// the end of the list, so we don't lose any data.
if (offset < 0) offset = merged.length;
// Now that we have a reliable offset, the rest of this logic
// is the same as in offsetLimitPagination.
for (let i = 0; i < incoming.length; ++i) {
merged[offset + i] = incoming[i];
}
return merged;
},
// // If you always want to return the whole list, you can omit
// // this read function.
// read(
// existing,
// { args: { cursor, limit = existing.length }, readField }
// ) {
// if (existing) {
// let offset = offsetFromCursor(existing, cursor, readField);
// // If we couldn't find the cursor, default to reading the
// // entire list.
// if (offset < 0) offset = 0;
// return existing.slice(offset, offset + limit);
// }
// },
},
},
},
},
}),
});
function offsetFromCursor(items, cursor, readField) {
// Search from the back of the list because the cursor we're
// looking for is typically the ID of the last item.
for (let i = items.length - 1; i >= 0; --i) {
const item = items[i];
// Using readField works for both non-normalized objects
// (returning item.id) and normalized references (returning
// the id field from the referenced entity object), so it's
// a good idea to use readField when you're not sure what
// kind of elements you're dealing with.
if (readField("id", item) === cursor) {
// Add one because the cursor identifies the item just
// before the first item in the page we care about.
return i + 1;
}
}
// Report that the cursor could not be found.
return -1;
}
Let's suppose I use this a field policy for the list of products, how do I go from here? I'm completely lost