
Rendering paginated data is a very common UI pattern to avoid overloading bandwidth or even your UI. React Query exposes a usePaginatedQuery that is very similar to useQuery that helps with this very scenario.
Consider the following example where we would ideally want to increment a pageIndex (or cursor) for a query. If we were to use useQuery, it would technically work fine, but the UI would jump in and out of the success and loading states as different queries are created and destroyed for each page or cursor. By using usePaginatedQuery we get a few new things:
data, you should use resolvedData instead. This is the data from the last known successful query result. As new page queries resolve, resolvedData remains available to show the last page's data while a new page is requested. When the new page data is received, resolvedData gets updated to the new page's data.latestData is available. When the desired page is being requested, latestData will be undefined until the query resolves, then it will get updated with the latest pages data result.function Todos() {const [page, setPage] = React.useState(0)const fetchProjects = (key, page = 0) => fetch('/api/projects?page=' + page)const {isLoading,isError,error,resolvedData,latestData,isFetching,} = usePaginatedQuery(['projects', page], fetchProjects)return (<div>{isLoading ? (<div>Loading...</div>) : isError ? (<div>Error: {error.message}</div>) : (// `resolvedData` will either resolve to the latest page's data// or if fetching a new page, the last successful page's data<div>{resolvedData.projects.map(project => (<p key={project.id}>{project.name}</p>))}</div>)}<span>Current Page: {page + 1}</span><buttononClick={() => setPage(old => Math.max(old - 1, 0))}disabled={page === 0}>Previous Page</button>{' '}<buttononClick={() =>// Here, we use `latestData` so the Next Page// button isn't relying on potentially old datasetPage(old => (!latestData || !latestData.hasMore ? old : old + 1))}disabled={!latestData || !latestData.hasMore}>Next Page</button>{// Since the last page's data potentially sticks around between page requests,// we can use `isFetching` to show a background loading// indicator since our `status === 'loading'` state won't be triggeredisFetching ? <span> Loading...</span> : null}{' '}</div>)}
The latest TanStack news, articles, and resources, sent to your inbox.