跳至主内容
版本:11.x

TanStack React Query

非官方测试版翻译

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

快速查询示例

tsx
import { useQuery } from '@tanstack/react-query';
import { useTRPC } from './trpc';
 
function Users() {
const trpc = useTRPC();
 
const greetingQuery = useQuery(trpc.greeting.queryOptions({ name: 'Jerry' }));
 
// greetingQuery.data === 'Hello Jerry'
}
tsx
import { useQuery } from '@tanstack/react-query';
import { useTRPC } from './trpc';
 
function Users() {
const trpc = useTRPC();
 
const greetingQuery = useQuery(trpc.greeting.queryOptions({ name: 'Jerry' }));
 
// greetingQuery.data === 'Hello Jerry'
}

用法

该客户端的设计理念是提供轻量级且类型安全的工厂方法,与 Tanstack React Query 原生无缝集成。这意味着只需跟随客户端提供的自动补全提示,您就能专注于开发工作,所需知识完全遵循 TanStack React Query 官方文档的内容。

tsx
export default function Basics() {
const trpc = useTRPC();
const queryClient = useQueryClient();
 
// Create QueryOptions which can be passed to query hooks
const myQueryOptions = trpc.path.to.query.queryOptions({ /** inputs */ })
const myQuery = useQuery(myQueryOptions)
// or:
// useSuspenseQuery(myQueryOptions)
// useInfiniteQuery(myQueryOptions)
 
// Create MutationOptions which can be passed to useMutation
const myMutationOptions = trpc.path.to.mutation.mutationOptions()
const myMutation = useMutation(myMutationOptions)
 
// Create a QueryKey which can be used to manipulate many methods
// on TanStack's QueryClient in a type-safe manner
const myQueryKey = trpc.path.to.query.queryKey()
 
const invalidateMyQueryKey = () => {
queryClient.invalidateQueries({ queryKey: myQueryKey })
}
 
return (
// Your app here
null
)
}
tsx
export default function Basics() {
const trpc = useTRPC();
const queryClient = useQueryClient();
 
// Create QueryOptions which can be passed to query hooks
const myQueryOptions = trpc.path.to.query.queryOptions({ /** inputs */ })
const myQuery = useQuery(myQueryOptions)
// or:
// useSuspenseQuery(myQueryOptions)
// useInfiniteQuery(myQueryOptions)
 
// Create MutationOptions which can be passed to useMutation
const myMutationOptions = trpc.path.to.mutation.mutationOptions()
const myMutation = useMutation(myMutationOptions)
 
// Create a QueryKey which can be used to manipulate many methods
// on TanStack's QueryClient in a type-safe manner
const myQueryKey = trpc.path.to.query.queryKey()
 
const invalidateMyQueryKey = () => {
queryClient.invalidateQueries({ queryKey: myQueryKey })
}
 
return (
// Your app here
null
)
}

trpc 对象具备完整的类型安全特性,可为 AppRouter 中的所有过程提供自动补全。在代理链末端,以下方法可供使用:

queryOptions - 数据查询

适用于所有查询过程。提供对 Tanstack 的 queryOptions 函数的类型安全封装。第一个参数是过程的输入值,第二个参数接受所有原生 Tanstack React Query 配置选项。

ts
const queryOptions = trpc.path.to.query.queryOptions(
{
/** input */
id: 'foo',
},
{
// Any Tanstack React Query options
staleTime: 1000,
},
);
ts
const queryOptions = trpc.path.to.query.queryOptions(
{
/** input */
id: 'foo',
},
{
// Any Tanstack React Query options
staleTime: 1000,
},
);

您还可以向 queryOptions 函数传递 trpc 对象,从而为客户端提供 tRPC 请求配置选项。

ts
const queryOptions = trpc.path.to.query.queryOptions(
{
/** input */
id: 'foo',
},
{
trpc: {
// Provide tRPC request options to the client
context: {
// see https://trpc.io/docs/client/links#managing-context
},
},
},
);
ts
const queryOptions = trpc.path.to.query.queryOptions(
{
/** input */
id: 'foo',
},
{
trpc: {
// Provide tRPC request options to the client
context: {
// see https://trpc.io/docs/client/links#managing-context
},
},
},
);

若需要以类型安全的方式禁用查询,可使用 skipToken

ts
const query = useQuery(
trpc.user.details.queryOptions(
user?.id && project?.id
? {
userId: user.id,
projectId: project.id,
}
: skipToken,
{
staleTime: 1000,
},
),
);
ts
const query = useQuery(
trpc.user.details.queryOptions(
user?.id && project?.id
? {
userId: user.id,
projectId: project.id,
}
: skipToken,
{
staleTime: 1000,
},
),
);

返回结果可传递给 useQueryuseSuspenseQuery 钩子,亦或是查询客户端方法如 fetchQueryprefetchQueryprefetchInfiniteQueryinvalidateQueries 等。

infiniteQueryOptions - 无限数据查询

适用于所有接收游标输入的查询过程。提供对 Tanstack 的 infiniteQueryOptions 函数的类型安全封装。第一个参数是过程的输入值,第二个参数接受所有原生 Tanstack React Query 配置选项。

ts
const infiniteQueryOptions = trpc.path.to.query.infiniteQueryOptions(
{
/** input */
},
{
// Any Tanstack React Query options
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
},
);
ts
const infiniteQueryOptions = trpc.path.to.query.infiniteQueryOptions(
{
/** input */
},
{
// Any Tanstack React Query options
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
},
);

queryKey - 获取查询键及执行查询客户端操作

适用于所有查询过程。允许以类型安全的方式访问查询键。

ts
const queryKey = trpc.path.to.query.queryKey();
ts
const queryKey = trpc.path.to.query.queryKey();

由于 Tanstack React Query 对查询键使用模糊匹配,您还可以创建任意子路径的部分查询键,以匹配属于特定路由器的所有查询:

ts
const queryKey = trpc.router.pathKey();
ts
const queryKey = trpc.router.pathKey();

甚至可以使用根路径匹配所有 tRPC 查询:

ts
const queryKey = trpc.pathKey();
ts
const queryKey = trpc.pathKey();

infiniteQueryKey - 获取无限查询键

适用于所有接收游标输入的查询过程。允许以类型安全的方式访问无限查询的查询键。

ts
const infiniteQueryKey = trpc.path.to.query.infiniteQueryKey({
/** input */
});
ts
const infiniteQueryKey = trpc.path.to.query.infiniteQueryKey({
/** input */
});

返回结果可用于查询客户端方法,如 getQueryDatasetQueryDatainvalidateQueries 等。

ts
// Get cached data for an infinite query
const cachedData = queryClient.getQueryData(
trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),
);
 
// Set cached data for an infinite query
queryClient.setQueryData(
trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),
(data) => {
// Modify the data
return data;
},
);
ts
// Get cached data for an infinite query
const cachedData = queryClient.getQueryData(
trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),
);
 
// Set cached data for an infinite query
queryClient.setQueryData(
trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),
(data) => {
// Modify the data
return data;
},
);

queryFilter - 创建查询过滤器

适用于所有查询过程。允许以类型安全的方式创建 查询过滤器

ts
const queryFilter = trpc.path.to.query.queryFilter(
{
/** input */
},
{
// Any Tanstack React Query filter
predicate: (query) => {
return !!query.state.data;
},
},
);
ts
const queryFilter = trpc.path.to.query.queryFilter(
{
/** input */
},
{
// Any Tanstack React Query filter
predicate: (query) => {
return !!query.state.data;
},
},
);

与查询键类似,若需在整个路由器范围执行过滤操作,可使用 pathFilter 来定位任意子路径。

ts
const queryFilter = trpc.path.pathFilter({
// Any Tanstack React Query filter
predicate: (query) => {
return !!query.state.data;
},
});
ts
const queryFilter = trpc.path.pathFilter({
// Any Tanstack React Query filter
predicate: (query) => {
return !!query.state.data;
},
});

此方法适用于创建可传递给客户端方法(如 queryClient.invalidateQueries 等)的过滤器。

infiniteQueryFilter - 创建无限查询过滤器

适用于所有接收游标输入的查询过程。允许以类型安全的方式为无限查询创建 查询过滤器

ts
const infiniteQueryFilter = trpc.path.to.query.infiniteQueryFilter(
{
/** input */
},
{
// Any Tanstack React Query filter
predicate: (query) => {
return !!query.state.data;
},
},
);
ts
const infiniteQueryFilter = trpc.path.to.query.infiniteQueryFilter(
{
/** input */
},
{
// Any Tanstack React Query filter
predicate: (query) => {
return !!query.state.data;
},
},
);

此方法适用于创建可传递给客户端方法(如 queryClient.invalidateQueries 等)的过滤器。

ts
await queryClient.invalidateQueries(
trpc.path.to.query.infiniteQueryFilter(
{},
{
predicate: (query) => {
// Filter logic based on query state
return query.state.status === 'success';
},
},
),
);
ts
await queryClient.invalidateQueries(
trpc.path.to.query.infiniteQueryFilter(
{},
{
predicate: (query) => {
// Filter logic based on query state
return query.state.status === 'success';
},
},
),
);

mutationOptions - 创建变更配置

适用于所有变更过程。提供类型安全的恒等函数,用于构建可传递给 useMutation 的配置选项。

ts
const mutationOptions = trpc.path.to.mutation.mutationOptions({
// Any Tanstack React Query options
onSuccess: (data) => {
// do something with the data
},
});
ts
const mutationOptions = trpc.path.to.mutation.mutationOptions({
// Any Tanstack React Query options
onSuccess: (data) => {
// do something with the data
},
});

mutationKey - 获取变更键

适用于所有变更过程。允许以类型安全的方式获取变更键。

ts
const mutationKey = trpc.path.to.mutation.mutationKey();
ts
const mutationKey = trpc.path.to.mutation.mutationKey();

subscriptionOptions - 创建订阅配置

由于 TanStack 未提供订阅钩子,因此我们在此继续提供自有的抽象层,该抽象层与 标准 tRPC 订阅配置 协同工作。 适用于所有订阅过程。提供类型安全的恒等函数,用于构建可传递给 useSubscription 的配置选项。 请注意,您需要在 tRPC 客户端中配置 httpSubscriptionLinkwsLink 方可使用订阅功能。

tsx
function SubscriptionExample() {
const trpc = useTRPC();
const subscription = useSubscription(
trpc.path.to.subscription.subscriptionOptions(
{
/** input */
},
{
enabled: true,
onStarted: () => {
// do something when the subscription is started
},
onData: (data) => {
// you can handle the data here
},
onError: (error) => {
// you can handle the error here
},
onConnectionStateChange: (state) => {
// you can handle the connection state here
},
},
),
);
 
// Or you can handle the state here
subscription.data; // The lastly received data
subscription.error; // The lastly received error
 
/**
* The current status of the subscription.
* Will be one of: `'idle'`, `'connecting'`, `'pending'`, or `'error'`.
*
* - `idle`: subscription is disabled or ended
* - `connecting`: trying to establish a connection
* - `pending`: connected to the server, receiving data
* - `error`: an error occurred and the subscription is stopped
*/
subscription.status;
 
// Reset the subscription (if you have an error etc)
subscription.reset();
 
return <>{/* ... */}</>;
}
tsx
function SubscriptionExample() {
const trpc = useTRPC();
const subscription = useSubscription(
trpc.path.to.subscription.subscriptionOptions(
{
/** input */
},
{
enabled: true,
onStarted: () => {
// do something when the subscription is started
},
onData: (data) => {
// you can handle the data here
},
onError: (error) => {
// you can handle the error here
},
onConnectionStateChange: (state) => {
// you can handle the connection state here
},
},
),
);
 
// Or you can handle the state here
subscription.data; // The lastly received data
subscription.error; // The lastly received error
 
/**
* The current status of the subscription.
* Will be one of: `'idle'`, `'connecting'`, `'pending'`, or `'error'`.
*
* - `idle`: subscription is disabled or ended
* - `connecting`: trying to establish a connection
* - `pending`: connected to the server, receiving data
* - `error`: an error occurred and the subscription is stopped
*/
subscription.status;
 
// Reset the subscription (if you have an error etc)
subscription.reset();
 
return <>{/* ... */}</>;
}

查询键前缀

当在单个应用中使用多个 tRPC 提供程序时(例如连接不同的后端服务),相同路径的查询会在缓存中发生冲突。启用查询键前缀功能可避免此问题。

tsx
// Without prefixes - these would collide!
const authQuery = useQuery(trpcAuth.list.queryOptions()); // auth service
const billingQuery = useQuery(trpcBilling.list.queryOptions()); // billing service
tsx
// Without prefixes - these would collide!
const authQuery = useQuery(trpcAuth.list.queryOptions()); // auth service
const billingQuery = useQuery(trpcBilling.list.queryOptions()); // billing service

在创建上下文时启用功能标志:

utils/trpc.ts
tsx
// [...]
 
const billing = createTRPCContext<BillingRouter, { keyPrefix: true }>();
export const BillingProvider = billing.TRPCProvider;
export const useBilling = billing.useTRPC;
export const createBillingClient = () =>
createTRPCClient<BillingRouter>({
links: [
/* ... */
],
});
 
const account = createTRPCContext<AccountRouter, { keyPrefix: true }>();
export const AccountProvider = account.TRPCProvider;
export const useAccount = account.useTRPC;
export const createAccountClient = () =>
createTRPCClient<AccountRouter>({
links: [
/* ... */
],
});
utils/trpc.ts
tsx
// [...]
 
const billing = createTRPCContext<BillingRouter, { keyPrefix: true }>();
export const BillingProvider = billing.TRPCProvider;
export const useBilling = billing.useTRPC;
export const createBillingClient = () =>
createTRPCClient<BillingRouter>({
links: [
/* ... */
],
});
 
const account = createTRPCContext<AccountRouter, { keyPrefix: true }>();
export const AccountProvider = account.TRPCProvider;
export const useAccount = account.useTRPC;
export const createAccountClient = () =>
createTRPCClient<AccountRouter>({
links: [
/* ... */
],
});
App.tsx
tsx
import { useState } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import {
BillingProvider,
AccountProvider,
createBillingClient,
createAccountClient,
} from './utils/trpc';
 
// [...]
 
export function App() {
const [queryClient] = useState(() => new QueryClient());
const [billingClient] = useState(() => createBillingClient());
const [accountClient] = useState(() => createAccountClient());
 
return (
<QueryClientProvider client={queryClient}>
<BillingProvider
trpcClient={billingClient}
queryClient={queryClient}
keyPrefix="billing"
>
<AccountProvider
trpcClient={accountClient}
queryClient={queryClient}
keyPrefix="account"
>
<div>{/* ... */}</div>
</AccountProvider>
</BillingProvider>
</QueryClientProvider>
);
}
App.tsx
tsx
import { useState } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import {
BillingProvider,
AccountProvider,
createBillingClient,
createAccountClient,
} from './utils/trpc';
 
// [...]
 
export function App() {
const [queryClient] = useState(() => new QueryClient());
const [billingClient] = useState(() => createBillingClient());
const [accountClient] = useState(() => createAccountClient());
 
return (
<QueryClientProvider client={queryClient}>
<BillingProvider
trpcClient={billingClient}
queryClient={queryClient}
keyPrefix="billing"
>
<AccountProvider
trpcClient={accountClient}
queryClient={queryClient}
keyPrefix="account"
>
<div>{/* ... */}</div>
</AccountProvider>
</BillingProvider>
</QueryClientProvider>
);
}
components/MyComponent.tsx
tsx
import { useQuery } from '@tanstack/react-query';
import { useBilling, useAccount } from '../utils/trpc';
 
// [...]
 
export function MyComponent() {
const billing = useBilling();
const account = useAccount();
 
const billingList = useQuery(billing.list.queryOptions());
const accountList = useQuery(account.list.queryOptions());
 
return (
<div>
<div>Billing: {JSON.stringify(billingList.data ?? null)}</div>
<div>Account: {JSON.stringify(accountList.data ?? null)}</div>
</div>
);
}
components/MyComponent.tsx
tsx
import { useQuery } from '@tanstack/react-query';
import { useBilling, useAccount } from '../utils/trpc';
 
// [...]
 
export function MyComponent() {
const billing = useBilling();
const account = useAccount();
 
const billingList = useQuery(billing.list.queryOptions());
const accountList = useQuery(account.list.queryOptions());
 
return (
<div>
<div>Billing: {JSON.stringify(billingList.data ?? null)}</div>
<div>Account: {JSON.stringify(accountList.data ?? null)}</div>
</div>
);
}

查询键将被正确添加前缀以避免冲突:

tsx
// Example of how the query keys look with prefixes
const queryKeys = [
[['billing'], ['list'], { type: 'query' }],
[['account'], ['list'], { type: 'query' }],
];
tsx
// Example of how the query keys look with prefixes
const queryKeys = [
[['billing'], ['list'], { type: 'query' }],
[['account'], ['list'], { type: 'query' }],
];

推断输入输出类型

当需要推断过程或路由器的输入输出类型时,根据场景提供两种方案:

推断整个路由器的输入输出类型

ts
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
import type { AppRouter } from './server/router';
 
export type Inputs = inferRouterInputs<AppRouter>;
export type Outputs = inferRouterOutputs<AppRouter>;
ts
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
import type { AppRouter } from './server/router';
 
export type Inputs = inferRouterInputs<AppRouter>;
export type Outputs = inferRouterOutputs<AppRouter>;

推断单个过程的类型

ts
import type { inferInput, inferOutput } from '@trpc/tanstack-react-query';
 
function Component() {
const trpc = useTRPC();
 
type Input = inferInput<typeof trpc.path.to.procedure>;
type Output = inferOutput<typeof trpc.path.to.procedure>;
}
ts
import type { inferInput, inferOutput } from '@trpc/tanstack-react-query';
 
function Component() {
const trpc = useTRPC();
 
type Input = inferInput<typeof trpc.path.to.procedure>;
type Output = inferOutput<typeof trpc.path.to.procedure>;
}

访问 tRPC 客户端

若您采用 基于 React Context 的配置方案,可通过 useTRPCClient 钩子访问 tRPC 客户端。

tsx
import { useTRPCClient } from './trpc';
 
async function Component() {
const trpcClient = useTRPCClient();
 
const result = await trpcClient.getUser.query({
id: '1',
});
}
tsx
import { useTRPCClient } from './trpc';
 
async function Component() {
const trpcClient = useTRPCClient();
 
const result = await trpcClient.getUser.query({
id: '1',
});
}

若您采用 无 React Context 的配置方案,则可直接导入全局客户端实例。

ts
import { client } from './trpc';
 
const result = await client.path.to.procedure.query({
/** input */
id: 'foo',
});
ts
import { client } from './trpc';
 
const result = await client.path.to.procedure.query({
/** input */
id: 'foo',
});