Use usePaginatedQuery instead of useQuery, if you want to divide large
data into smaller contiguous intervals of data. For instance, you have
many numbered pages, and you want to show the first three pages initially.
After clicking on Show next pages, the user should see the following
three pages only. For this case, you can usePaginatedQuery as shown in
the following example.
import { Suspense } from "react"
import {
usePaginatedQuery,
useRouterQuery,
useRouter,
} from "@blitzjs/rpc"
import { Link, BlitzPage, Routes } from "blitz"
import getProjects from "app/products/queries/getProjects"
const ITEMS_PER_PAGE = 3
const Projects = () => {
const router = useRouter()
const page = Number(router.query.page) || 0
const [{ projects, hasMore }] = usePaginatedQuery(getProjects, {
orderBy: { id: "asc" },
skip: ITEMS_PER_PAGE * page,
take: ITEMS_PER_PAGE,
})
const goToPreviousPage = () =>
router.push({ query: { page: page - 1 } })
const goToNextPage = () => router.push({ query: { page: page + 1 } })
return (
<div>
{projects.map((project) => (
<p key={project.id}>
<Link href={Routes.Project({ handle: project.handle })}>
<a>{project.name}</a>
</Link>
</p>
))}
<button disabled={page === 0} onClick={goToPreviousPage}>
Previous
</button>
<button disabled={!hasMore} onClick={goToNextPage}>
Next
</button>
</div>
)
}And here's the query to work with that:
import { paginate, resolver } from "blitz"
import db, { Prisma } from "db"
interface GetProjectsInput
extends Pick<
Prisma.ProjectFindManyArgs,
"where" | "orderBy" | "skip" | "take"
> {}
export default resolver.pipe(
resolver.authorize(),
async ({ where, orderBy, skip = 0, take = 100 }: GetProjectsInput) => {
const {
items: projects,
hasMore,
nextPage,
count,
} = await paginate({
skip,
take,
count: () => db.project.count({ where }),
query: (paginateArgs) =>
db.project.findMany({ ...paginateArgs, where, orderBy }),
})
return {
projects,
nextPage,
hasMore,
count,
}
}
)const [
queryResult,
{
dataUpdatedAt,
error,
errorUpdatedAt,
failureCount,
isError,
isFetched,
isFetchedAfterMount,
isFetching,
isIdle,
isLoading,
isLoadingError,
isPlaceholderData,
isPreviousData,
isRefetchError,
isStale,
isSuccess,
refetch,
remove,
status,
setQueryData,
},
] = usePaginatedQuery(queryResolver, queryInputArguments, options)queryResolver: A Blitz query resolverqueryInputArgumentsqueryResolveroptionsThe options are identical to the options for the useQuery hook
[queryResult, queryExtras]
queryResult: TDataundefined.queryExtras: Objectstatus: Stringidle if the query is idle. This only happens if a query is
initialized with enabled: false and no initial data is available.loading if the query is in a "hard" loading state. This means
there is no cached data and the query is currently fetching, eg
isFetching === trueerror if the query attempt resulted in an error. The corresponding
error property has the error received from the attempted fetchsuccess if the query has received a response with no errors and is
ready to display its data. The corresponding data property on the
query is the data received from the successful fetch or if the
query's enabled property is set to false and has not been
fetched yet data is the first initialData supplied to the query
on initialization.isIdle: booleanstatus variable above, provided for
convenience.isLoading: booleanstatus variable above, provided for
convenience.isSuccess: booleanstatus variable above, provided for
convenience.isError: booleanstatus variable above, provided for
convenience.isLoadingError: booleantrue if the query failed while fetching for the first time.isRefetchError: booleantrue if the query failed while refetching.data: TDataundefined.dataUpdatedAt: numberstatus
as "success".error: null | TErrornullerrorUpdatedAt: numberstatus
as "error".isStale: booleantrue if the data in the cache is invalidated or if the data
is older than the given staleTime.isPlaceholderData: booleantrue if the data shown is the placeholder data.isPreviousData: booleantrue when data from the previous query is returned.isFetched: booleantrue if the query has been fetched.isFetchedAfterMount: booleantrue if the query has been fetched after the component
mounted.isFetching: booleantrue so long as enabled is set to falsetrue if the query is currently fetching, including
background fetching.failureCount: number0 when the query succeeds.refetch: (options: { throwOnError: boolean, cancelRefetch: boolean }) => Promise<UseQueryResult>throwOnError: true optioncancelRefetch is true, then the current request will be
cancelled before a new request is maderemove: () => voidsetQueryData() - Function(newData, opts) => Promise<void>newData can be an object of new data or a function that receives the
old data and returns the new datarefetch() to
ensure the data is correct. Disable refetch by passing an options
object {refetch: false} as the second argument.setQueryData().