useInfiniteQuery()
info
- Your procedure needs to accept a
cursorinput of any type (string,number, etc) to expose this hook. - For more details on infinite queries read the react-query docs
- In this example we're using Prisma - see their docs on cursor-based pagination
Example Procedure
server/routers/_app.tstsximport {initTRPC } from '@trpc/server';import {z } from 'zod';export constt =initTRPC .create ();export constappRouter =t .router ({infinitePosts :t .procedure .input (z .object ({limit :z .number ().min (1).max (100).nullish (),cursor :z .number ().nullish (), // <-- "cursor" needs to exist, but can be any typedirection :z .enum (['forward', 'backward']), // optional, useful for bi-directional query}),).query (async (opts ) => {const {input } =opts ;constlimit =input .limit ?? 50;const {cursor } =input ;constitems = awaitprisma .post .findMany ({take :limit + 1, // get an extra item at the end which we'll use as next cursorwhere : {title : {contains : 'Prisma' /* Optional filter */,},},cursor :cursor ? {myCursor :cursor } :undefined ,orderBy : {myCursor : 'asc',},});letnextCursor : typeofcursor | undefined =undefined ;if (items .length >limit ) {constnextItem =items .pop ();nextCursor =nextItem !.myCursor ;}return {items ,nextCursor ,};}),});
server/routers/_app.tstsximport {initTRPC } from '@trpc/server';import {z } from 'zod';export constt =initTRPC .create ();export constappRouter =t .router ({infinitePosts :t .procedure .input (z .object ({limit :z .number ().min (1).max (100).nullish (),cursor :z .number ().nullish (), // <-- "cursor" needs to exist, but can be any typedirection :z .enum (['forward', 'backward']), // optional, useful for bi-directional query}),).query (async (opts ) => {const {input } =opts ;constlimit =input .limit ?? 50;const {cursor } =input ;constitems = awaitprisma .post .findMany ({take :limit + 1, // get an extra item at the end which we'll use as next cursorwhere : {title : {contains : 'Prisma' /* Optional filter */,},},cursor :cursor ? {myCursor :cursor } :undefined ,orderBy : {myCursor : 'asc',},});letnextCursor : typeofcursor | undefined =undefined ;if (items .length >limit ) {constnextItem =items .pop ();nextCursor =nextItem !.myCursor ;}return {items ,nextCursor ,};}),});
Example React Component
components/MyComponent.tsxtsximport {trpc } from '../utils/trpc';export functionMyComponent () {constmyQuery =trpc .infinitePosts .useInfiniteQuery ({limit : 10,},{getNextPageParam : (lastPage ) =>lastPage .nextCursor ,// initialCursor: 1, // <-- optional you can pass an initialCursor},);// [...]}
components/MyComponent.tsxtsximport {trpc } from '../utils/trpc';export functionMyComponent () {constmyQuery =trpc .infinitePosts .useInfiniteQuery ({limit : 10,},{getNextPageParam : (lastPage ) =>lastPage .nextCursor ,// initialCursor: 1, // <-- optional you can pass an initialCursor},);// [...]}
Helpers
getInfiniteData()
This helper gets the currently cached data from an existing infinite query
components/MyComponent.tsxtsximport {trpc } from '../utils/trpc';export functionMyComponent () {constutils =trpc .useUtils ();constmyMutation =trpc .infinitePosts .add .useMutation ({asynconMutate (opts ) {awaitutils .infinitePosts .list .cancel ();constallPosts =utils .infinitePosts .list .getInfiniteData ({limit : 10 });// [...]},});}
components/MyComponent.tsxtsximport {trpc } from '../utils/trpc';export functionMyComponent () {constutils =trpc .useUtils ();constmyMutation =trpc .infinitePosts .add .useMutation ({asynconMutate (opts ) {awaitutils .infinitePosts .list .cancel ();constallPosts =utils .infinitePosts .list .getInfiniteData ({limit : 10 });// [...]},});}
setInfiniteData()
This helper allows you to update a query's cached data
components/MyComponent.tsxtsximport {trpc } from '../utils/trpc';export functionMyComponent () {constutils =trpc .useUtils ();constmyMutation =trpc .infinitePosts .delete .useMutation ({asynconMutate (opts ) {awaitutils .infinitePosts .list .cancel ();utils .infinitePosts .list .setInfiniteData ({limit : 10 }, (data ) => {if (!data ) {return {pages : [],pageParams : [],};}return {...data ,pages :data .pages .map ((page ) => ({...page ,items :page .items .filter ((item ) =>item .status === 'published'),})),};});},});// [...]}
components/MyComponent.tsxtsximport {trpc } from '../utils/trpc';export functionMyComponent () {constutils =trpc .useUtils ();constmyMutation =trpc .infinitePosts .delete .useMutation ({asynconMutate (opts ) {awaitutils .infinitePosts .list .cancel ();utils .infinitePosts .list .setInfiniteData ({limit : 10 }, (data ) => {if (!data ) {return {pages : [],pageParams : [],};}return {...data ,pages :data .pages .map ((page ) => ({...page ,items :page .items .filter ((item ) =>item .status === 'published'),})),};});},});// [...]}