Reputation: 2224
I have implemented a limit-offset paginated query in my server that I am querying from the front end. The pagination seems to work fine but caching is not working as I would expect it to.
My assumption of the expected behavior is that when I request the next page, the next page's worth of data will be fetched from the server, but when I hit the back button, the query should only fetch from the cache as that data has previously been fetched from the server (when that same page was previously requested), and so no query to the server is required.
This is somewhat the case in my app but not entirely. When I go forward a page I can see that it takes time for the page to populate meaning that the server is being queried hence the load time. When I go back a page, it is populated instantly meaning that the page data was fetched from the cache as expected, yet, when I console log from the server in that query and also look at the network in chrome dev tools, I can see that the client is in fact querying the server even though it also reads the data from the cache.
If the data has previously been queried with the same offset and limit variables, I do not want the client to fetch from the server, just from the cache.
This is my cache:
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
privateCloudProjectsPaginated: offsetLimitPagination(),
},
},
},
});
And this is the client component:
export default function Projects() {
const { debouncedSearch } = useContext(SearchContext);
const { filter } = useContext(FilterContext);
const { loading, data, fetchMor, error } = useQuery(ALL_PROJECTS, {
nextFetchPolicy: "cache-first",
variables: {
offset: 0,
limit: 10,
},
});
const getNextPage = useCallback(
(page, pageSize) => {
fetchMore({
variables: {
offset: page * pageSize,
limit: pageSize,
},
});
},
[filter, debouncedSearch, fetchMore]
);
if (error) {
return <Alert error={error} />;
}
return (
<StickyTable
onClickPath={"/private-cloud/admin/project/"}
onNextPage={getNextPage}
columns={columns}
rows={
loading ? [] : data.privateCloudProjectsPaginated.map(projectsToRows)
}
count={loading ? 0 : data.privateCloudProjectsCount}
title="Projects"
loading={loading}
/>
);
}
I have tried many fetchPolicy
and nextFetchPolicy
and nothing works. Also, my table component handles the slicing of the projects data so the cache just returns all the existing data
Upvotes: 1
Views: 1440
Reputation: 1817
In this case, to limit each query's result to only the items you requested, you can include a paginated read
function in your field policy. As suggested in the docs, because the offsetLimitPagination
helper is currently defining your field policy, you can combine your read function with the helper's result, like so:
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
privateCloudProjectsPaginated: {
...offsetLimitPagination(),
read(existing, { args }) {
// Implement here
}
},
},
},
},
});
This should allow for the client-side re-pagination of the list. It may be helpful to reference the docs' more detailed examples of what the read
function might look like. An important comment from these examples to flag:
A read function should always return undefined if existing is undefined. Returning undefined signals that the field is missing from the cache, which instructs Apollo Client to fetch its value from your GraphQL server.
In all events, if you use a paginated read function, its important to properly updating your offset and limit variables as required by your use case to prevent duplicate page renders.
Upvotes: 2