I have a page that contains several horizontal scrolling lists of items (let's call them blog posts, or whatever). I want each list to have pagination, but I don't want to make n HTTP requests when the page loads, and I also don't want to re-request the other lists when I'm loading the next page of results for one of them.
Here's what the query looks like:
const QUERY = gql`
query Sections(
$favoritesOffset: Int = 0
$featuredOffset: Int = 0
$recentOffset: Int = 0
) {
favorites: events(limit: 5, offset: $favoritesOffset, search: "FAVORITES") {
...listResponse
}
featured: events(limit: 5, offset: $featuredOffset, search: "FEATURED") {
...listResponse
}
recent: events(limit: 10, offset: $recentOffset, search: "RECENT") {
...listResponse
}
}
`;
I'm currently using Apollo client & server.
I want to be able to make one request for all 3 lists when the page loads and then use fetchMore
to only load the next page of one of them.
So far, my solution is to create one top-level query that contains all sections and then additional queries for the paginated responses of each section, then merged the data from the additional queries.
Using components makes this a bit cleaner.
Parent.tsx - With top-level query:
const QUERY = gql`
query Sections(
$favoritesOffset: Int = 0
$featuredOffset: Int = 0
$recentOffset: Int = 0
) {
favorites: events(limit: 5, offset: $favoritesOffset, search: "FAVORITES") {
...listResponse
}
featured: events(limit: 5, offset: $featuredOffset, search: "FEATURED") {
...listResponse
}
recent: events(limit: 10, offset: $recentOffset, search: "RECENT") {
...listResponse
}
}
${LIST_RESPONSE}
`;
const Page = () => {
const { data, loading } = useQuery(QUERY);
return (
<>
<Section initialData={results. favorites} search="FAVORITES" />
<Section initialData={results. featured} search="FEATURED" />
<Section initialData={results. recent} search="RECENT" />
</>
}
}
Section.tsx
const FETCH_MORE = gql`
query SectionsFetchMore($search: String, $offset: Int, $limit: Int = 10) {
...listResponse
}
${LIST_RESPONSE}
`;
const Section = ({search, initialData}) => {
const { data: fetchedData, fetchMore } = useQuery(FETCH_MORE, {
skip: true, // IMPORTANT
variables: { search, limit: 10, offset: 0 },
});
const fetchNextPage = useCallback(async (nextOffset) => {
await fetchMore({ variables: { offset: nextOffset } });
// Then merge data, either:
// + use client.cache.updateQuery() to update parent cache
// + keep local state that combines initialData and fetchedData
}, [fetchMore]);
return (
// ... display list ...
);
};
In my case, I merged the cache locally to keep things simple:
const data = useMemo(() => {
if (!fetchedData?.sections) {
return initialData;
}
return {
nodes: [...initialData.nodes, ...fetchedData?.sections?.nodes],
pageInfo: fetchedData.sections.pageInfo,
};
}, [fetchedData, initialData]);
But, you could also update the apollo cache using client.cache.updateQuery()
(more info).