跳至主内容
版本:11.x

TanStack React Query

非官方测试版翻译

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

Compared to our classic React Query Integration this client is simpler and more TanStack Query-native, providing factories for common TanStack React Query interfaces like QueryKeys, QueryOptions, and MutationOptions. We think it's the future and recommend using this over the classic client, read the announcement post for more information about this change.

快速查询示例

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 manipulated 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
)
}
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 manipulated 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
)
}

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

queryOptions - 数据查询

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

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

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

ts
const queryOptions = trpc.path.to.query.queryOptions(
{
/** input */
},
{
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 */
},
{
trpc: {
// Provide tRPC request options to the client
context: {
// see https://trpc.io/docs/client/links#managing-context
},
},
},
);

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

ts
import { skipToken } from '@tanstack/react-query';
const query = useQuery(
trpc.user.details.queryOptions(
user?.id && project?.id
? {
userId: user.id,
projectId: project.id,
}
: skipToken,
{
staleTime: 1000,
},
),
);
ts
import { skipToken } from '@tanstack/react-query';
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
const queryClient = useQueryClient();
// 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
const queryClient = useQueryClient();
// 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) => {
query.state.data;
},
},
);
ts
const queryFilter = trpc.path.to.query.queryFilter(
{
/** input */
},
{
// Any Tanstack React Query filter
predicate: (query) => {
query.state.data;
},
},
);

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

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

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

infiniteQueryFilter - 创建无限查询过滤器

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

ts
const infiniteQueryFilter = trpc.path.to.query.infiniteQueryFilter(
{
/** input */
},
{
// Any Tanstack React Query filter
predicate: (query) => {
query.state.data;
},
},
);
ts
const infiniteQueryFilter = trpc.path.to.query.infiniteQueryFilter(
{
/** input */
},
{
// Any Tanstack React Query filter
predicate: (query) => {
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.data?.pages.length > 0;
},
},
),
);
ts
await queryClient.invalidateQueries(
trpc.path.to.query.infiniteQueryFilter(
{},
{
predicate: (query) => {
// Filter logic based on query state
return query.state.data?.pages.length > 0;
},
},
),
);

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 的选项。请注意,必须配置 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
// [...]
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"
>
{/* ... */}
</AccountProvider>
</BillingProvider>
</QueryClientProvider>
);
}
App.tsx
tsx
// [...]
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"
>
{/* ... */}
</AccountProvider>
</BillingProvider>
</QueryClientProvider>
);
}
components/MyComponent.tsx
tsx
// [...]
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
// [...]
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 { AppRouter } from './path/to/server';
export type Inputs = inferRouterInputs<AppRouter>;
export type Outputs = inferRouterOutputs<AppRouter>;
ts
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
import { AppRouter } from './path/to/server';
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';
function Component() {
const trpcClient = useTRPCClient();
const result = await trpcClient.path.to.procedure.query({
/** input */
});
}
tsx
import { useTRPCClient } from './trpc';
function Component() {
const trpcClient = useTRPCClient();
const result = await trpcClient.path.to.procedure.query({
/** input */
});
}

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

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