Infinite Query Hooks
“Infinite” queries provide an extra layer of functionality on top of regular query hooks, to provide “load more” functionality on queries which are likely to return a lot of results.
WPGraphQL uses the Relay specification, which prefers to use a “cursor” (the ID of the last object), rather than the traditional “page number” approach — making it harder to build more traditional numbered pagination systems, but easier to build “load more” style pagination systems. You can read more about the justification for this here .
Any query which has Infinite in the name (eg. UseInfinitePosts) will be automatically generated as an infinite query hook. The query file naming conventions still applies to infinite queries.
Hook API
These hooks work just like regular query hooks, but have some extra properties available:
type UseInfiniteQuery<T> = UseQuery<T> & {
/**
* Will be `true` if there is a next page to be fetched (known via the `getNextPageParam` option).
*/
hasNextPage: boolean;
/**
* Will be `true` while fetching the next page with `fetchNextPage`.
*/
isFetchingNextPage: boolean;
/**
* This function allows you to fetch the next "page" of results.
*/
fetchNextPage: () => void
}Requirements
There are a few special requirements for infinite hooks, but we’ve configured the dev tooling to warn you if you’re not following these conventions (on top of the usual naming convention requirements)
Query Requirements:
- The query name must contain the word ‘Infinite’ (eg.
UseInfinitePosts) - There must be a
$limit: Intvariable defined on the query, with a default value. This is the number of items to load per page. - There must be a
$cursor: Stringvariable defined on the query, with no default value. This is used for controlling pagination. - The
$cursorvariable must be used in theafterfield of the query. - The
$limitvariable must be used in thefirstfield of the query. - Your query must select
nodes - Your query must select the
endCursorandhasNextPagefields from thepageInfoobject
Example Usage
Here’s an example query which fits all of those requirements (it’s pretty simple!):
query UseInfinitePosts($limit: Int = 6, $cursor: String) {
posts(first: $limit, after: $cursor) {
pageInfo {
endCursor
hasNextPage
}
nodes {
uri
title
}
}
}Unlike regular queries, the data property will not contain the full result — but will instead be an object with nodes (an array of results, built from all the pages loaded so far), and an optional total (if selected and supported, which will be the total results available).
To built a complete UI, you should also render a “load more” button if hasNextPage is true, and call loadNextPage() when the button is clicked. Alternatively, you can achieve an infinite-scroll UI, similar to Instagram/Facebook/Twitter by using an intersection observer sensor instead of a button.
Heres’ an example component which uses the query above:
import { useInfinitePosts } from "@hooks/queries"
function InfiniteExample() {
const lookup = useInfinitePosts()
return (
<div>
{/* The initial load is in progress, show some loading indicator */}
{lookup.isLoading && <div>Loading...</div>}
{/* The list of items, which will grow as more items are loaded */}
{lookup.isSuccess &&<ul>
{lookup.data?.nodes?.map((item, key) => {
return <li key={key}>{item.title}</li>
})}
</ul>}
{/* A 'Load more' button, which is disabled and shows 'Loading...' when more items are loading */}
{posts.hasNextPage && (
<button disabled={posts.isFetchingNextPage} onClick={() => posts.fetchNextPage()}>
{posts.isFetchingNextPage ? "Loading..." : "Load more"}
</button>
)}
</div>
)
}