跳至主内容
版本:11.x

useInfiniteQuery

非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

信息
  • 你的过程需要接受任意类型的 cursor 输入(stringnumber 等)才能使用此钩子
  • 有关无限查询的更多细节,请阅读 react-query 文档
  • 本示例使用 Prisma - 参考其 基于游标的分页文档

示例过程

server/routers/_app.ts
tsx
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
import { Context } from './[trpc]';
export const t = initTRPC.create();
export const appRouter = 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 type
direction: z.enum(['forward', 'backward']), // optional, useful for bi-directional query
}),
)
.query(async (opts) => {
const { input } = opts;
const limit = input.limit ?? 50;
const { cursor } = input;
const items = await prisma.post.findMany({
take: limit + 1, // get an extra item at the end which we'll use as next cursor
where: {
title: {
contains: 'Prisma' /* Optional filter */,
},
},
cursor: cursor ? { myCursor: cursor } : undefined,
orderBy: {
myCursor: 'asc',
},
});
let nextCursor: typeof cursor | undefined = undefined;
if (items.length > limit) {
const nextItem = items.pop();
nextCursor = nextItem!.myCursor;
}
return {
items,
nextCursor,
};
}),
});
server/routers/_app.ts
tsx
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
import { Context } from './[trpc]';
export const t = initTRPC.create();
export const appRouter = 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 type
direction: z.enum(['forward', 'backward']), // optional, useful for bi-directional query
}),
)
.query(async (opts) => {
const { input } = opts;
const limit = input.limit ?? 50;
const { cursor } = input;
const items = await prisma.post.findMany({
take: limit + 1, // get an extra item at the end which we'll use as next cursor
where: {
title: {
contains: 'Prisma' /* Optional filter */,
},
},
cursor: cursor ? { myCursor: cursor } : undefined,
orderBy: {
myCursor: 'asc',
},
});
let nextCursor: typeof cursor | undefined = undefined;
if (items.length > limit) {
const nextItem = items.pop();
nextCursor = nextItem!.myCursor;
}
return {
items,
nextCursor,
};
}),
});

示例 React 组件

components/MyComponent.tsx
tsx
import { trpc } from '../utils/trpc';
export function MyComponent() {
const myQuery = trpc.infinitePosts.useInfiniteQuery(
{
limit: 10,
},
{
getNextPageParam: (lastPage) => lastPage.nextCursor,
// initialCursor: 1, // <-- optional you can pass an initialCursor
},
);
// [...]
}
components/MyComponent.tsx
tsx
import { trpc } from '../utils/trpc';
export function MyComponent() {
const myQuery = trpc.infinitePosts.useInfiniteQuery(
{
limit: 10,
},
{
getNextPageParam: (lastPage) => lastPage.nextCursor,
// initialCursor: 1, // <-- optional you can pass an initialCursor
},
);
// [...]
}

辅助函数

getInfiniteData()

该辅助函数从现有无限查询中获取当前缓存的数据

components/MyComponent.tsx
tsx
import { trpc } from '../utils/trpc';
export function MyComponent() {
const utils = trpc.useUtils();
const myMutation = trpc.infinitePosts.add.useMutation({
async onMutate(opts) {
await utils.infinitePosts.cancel();
const allPosts = utils.infinitePosts.getInfiniteData({ limit: 10 });
// [...]
},
});
}
components/MyComponent.tsx
tsx
import { trpc } from '../utils/trpc';
export function MyComponent() {
const utils = trpc.useUtils();
const myMutation = trpc.infinitePosts.add.useMutation({
async onMutate(opts) {
await utils.infinitePosts.cancel();
const allPosts = utils.infinitePosts.getInfiniteData({ limit: 10 });
// [...]
},
});
}

setInfiniteData()

此辅助工具允许更新查询的缓存数据

components/MyComponent.tsx
tsx
import { trpc } from '../utils/trpc';
export function MyComponent() {
const utils = trpc.useUtils();
const myMutation = trpc.infinitePosts.delete.useMutation({
async onMutate(opts) {
await utils.infinitePosts.cancel();
utils.infinitePosts.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.tsx
tsx
import { trpc } from '../utils/trpc';
export function MyComponent() {
const utils = trpc.useUtils();
const myMutation = trpc.infinitePosts.delete.useMutation({
async onMutate(opts) {
await utils.infinitePosts.cancel();
utils.infinitePosts.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'),
})),
};
});
},
});
// [...]
}