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.
快速查询示例
tsximport { 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'}
tsximport { 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 官方文档的内容。
tsxexport default function Basics() {const trpc = useTRPC();const queryClient = useQueryClient();// Create QueryOptions which can be passed to query hooksconst myQueryOptions = trpc.path.to.query.queryOptions({ /** inputs */ })const myQuery = useQuery(myQueryOptions)// or:// useSuspenseQuery(myQueryOptions)// useInfiniteQuery(myQueryOptions)// Create MutationOptions which can be passed to useMutationconst 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 mannerconst myQueryKey = trpc.path.to.query.queryKey()const invalidateMyQueryKey = () => {queryClient.invalidateQueries({ queryKey: myQueryKey })}return (// Your app here)}
tsxexport default function Basics() {const trpc = useTRPC();const queryClient = useQueryClient();// Create QueryOptions which can be passed to query hooksconst myQueryOptions = trpc.path.to.query.queryOptions({ /** inputs */ })const myQuery = useQuery(myQueryOptions)// or:// useSuspenseQuery(myQueryOptions)// useInfiniteQuery(myQueryOptions)// Create MutationOptions which can be passed to useMutationconst 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 mannerconst myQueryKey = trpc.path.to.query.queryKey()const invalidateMyQueryKey = () => {queryClient.invalidateQueries({ queryKey: myQueryKey })}return (// Your app here)}
trpc 对象具备完整的类型安全特性,可为 AppRouter 中的所有过程提供自动补全。在代理链末端,以下方法可供使用:
queryOptions - 数据查询
适用于所有查询过程。提供对 Tanstack 的 queryOptions 函数的类型安全封装。第一个参数是过程的输入值,第二个参数接受所有原生 Tanstack React Query 配置选项。
tsconst queryOptions = trpc.path.to.query.queryOptions({/** input */},{// Any Tanstack React Query optionsstaleTime: 1000,},);
tsconst queryOptions = trpc.path.to.query.queryOptions({/** input */},{// Any Tanstack React Query optionsstaleTime: 1000,},);
您还可以向 queryOptions 函数传递 trpc 对象,从而为客户端提供 tRPC 请求配置选项。
tsconst queryOptions = trpc.path.to.query.queryOptions({/** input */},{trpc: {// Provide tRPC request options to the clientcontext: {// see https://trpc.io/docs/client/links#managing-context},},},);
tsconst queryOptions = trpc.path.to.query.queryOptions({/** input */},{trpc: {// Provide tRPC request options to the clientcontext: {// see https://trpc.io/docs/client/links#managing-context},},},);
若需要以类型安全的方式禁用查询,可使用 skipToken:
tsimport { 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,},),);
tsimport { 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,},),);
返回结果可传递给 useQuery 或 useSuspenseQuery 钩子,亦或是查询客户端方法如 fetchQuery、prefetchQuery、prefetchInfiniteQuery、invalidateQueries 等。
infiniteQueryOptions - 无限数据查询
适用于所有接收游标输入的查询过程。提供对 Tanstack 的 infiniteQueryOptions 函数的类型安全封装。第一个参数是过程的输入值,第二个参数接受所有原生 Tanstack React Query 配置选项。
tsconst infiniteQueryOptions = trpc.path.to.query.infiniteQueryOptions({/** input */},{// Any Tanstack React Query optionsgetNextPageParam: (lastPage, pages) => lastPage.nextCursor,},);
tsconst infiniteQueryOptions = trpc.path.to.query.infiniteQueryOptions({/** input */},{// Any Tanstack React Query optionsgetNextPageParam: (lastPage, pages) => lastPage.nextCursor,},);
queryKey - 获取查询键及执行查询客户端操作
适用于所有查询过程。允许以类型安全的方式访问查询键。
tsconst queryKey = trpc.path.to.query.queryKey();
tsconst queryKey = trpc.path.to.query.queryKey();
由于 Tanstack React Query 对查询键使用模糊匹配,您还可以创建任意子路径的部分查询键,以匹配属于特定路由器的所有查询:
tsconst queryKey = trpc.router.pathKey();
tsconst queryKey = trpc.router.pathKey();
甚至可以使用根路径匹配所有 tRPC 查询:
tsconst queryKey = trpc.pathKey();
tsconst queryKey = trpc.pathKey();
infiniteQueryKey - 获取无限查询键
适用于所有接收游标输入的查询过程。允许以类型安全的方式访问无限查询的查询键。
tsconst infiniteQueryKey = trpc.path.to.query.infiniteQueryKey({/** input */});
tsconst infiniteQueryKey = trpc.path.to.query.infiniteQueryKey({/** input */});
返回结果可用于查询客户端方法,如 getQueryData、setQueryData、invalidateQueries 等。
tsconst queryClient = useQueryClient();// Get cached data for an infinite queryconst cachedData = queryClient.getQueryData(trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),);// Set cached data for an infinite queryqueryClient.setQueryData(trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),(data) => {// Modify the datareturn data;},);
tsconst queryClient = useQueryClient();// Get cached data for an infinite queryconst cachedData = queryClient.getQueryData(trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),);// Set cached data for an infinite queryqueryClient.setQueryData(trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),(data) => {// Modify the datareturn data;},);
queryFilter - 创建查询过滤器
适用于所有查询过程。允许以类型安全的方式创建 查询过滤器。
tsconst queryFilter = trpc.path.to.query.queryFilter({/** input */},{// Any Tanstack React Query filterpredicate: (query) => {query.state.data;},},);
tsconst queryFilter = trpc.path.to.query.queryFilter({/** input */},{// Any Tanstack React Query filterpredicate: (query) => {query.state.data;},},);
与查询键类似,若需在整个路由器范围执行过滤操作,可使用 pathFilter 来定位任意子路径。
tsconst queryFilter = trpc.path.pathFilter({// Any Tanstack React Query filterpredicate: (query) => {query.state.data;},});
tsconst queryFilter = trpc.path.pathFilter({// Any Tanstack React Query filterpredicate: (query) => {query.state.data;},});
此方法适用于创建可传递给客户端方法(如 queryClient.invalidateQueries 等)的过滤器。
infiniteQueryFilter - 创建无限查询过滤器
适用于所有接收游标输入的查询过程。允许以类型安全的方式为无限查询创建 查询过滤器。
tsconst infiniteQueryFilter = trpc.path.to.query.infiniteQueryFilter({/** input */},{// Any Tanstack React Query filterpredicate: (query) => {query.state.data;},},);
tsconst infiniteQueryFilter = trpc.path.to.query.infiniteQueryFilter({/** input */},{// Any Tanstack React Query filterpredicate: (query) => {query.state.data;},},);
此方法适用于创建可传递给客户端方法(如 queryClient.invalidateQueries 等)的过滤器。
tsawait queryClient.invalidateQueries(trpc.path.to.query.infiniteQueryFilter({},{predicate: (query) => {// Filter logic based on query statereturn query.state.data?.pages.length > 0;},},),);
tsawait queryClient.invalidateQueries(trpc.path.to.query.infiniteQueryFilter({},{predicate: (query) => {// Filter logic based on query statereturn query.state.data?.pages.length > 0;},},),);
mutationOptions - 创建变更配置
适用于所有变更过程。提供类型安全的标识函数,用于构建可传递给 useMutation 的配置选项。
tsconst mutationOptions = trpc.path.to.mutation.mutationOptions({// Any Tanstack React Query optionsonSuccess: (data) => {// do something with the data},});
tsconst mutationOptions = trpc.path.to.mutation.mutationOptions({// Any Tanstack React Query optionsonSuccess: (data) => {// do something with the data},});
mutationKey - 获取变更键
适用于所有变更过程。允许以类型安全的方式获取变更键。
tsconst mutationKey = trpc.path.to.mutation.mutationKey();
tsconst mutationKey = trpc.path.to.mutation.mutationKey();
subscriptionOptions - 创建订阅配置
TanStack 未提供订阅钩子,因此我们继续在此提供自研的抽象层,该抽象层与标准 tRPC 订阅设置协同工作。适用于所有订阅过程,提供类型安全的标识函数用于构建可传递给 useSubscription 的选项。请注意,必须配置 httpSubscriptionLink 或 wsLink 才能使用订阅功能。
tsxfunction 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 heresubscription.data; // The lastly received datasubscription.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 <>{/* ... */}</>;}
tsxfunction 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 heresubscription.data; // The lastly received datasubscription.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 serviceconst billingQuery = useQuery(trpcBilling.list.queryOptions()); // billing service
tsx// Without prefixes - these would collide!const authQuery = useQuery(trpcAuth.list.queryOptions()); // auth serviceconst billingQuery = useQuery(trpcBilling.list.queryOptions()); // billing service
在创建上下文时启用功能标志:
utils/trpc.tstsx// [...]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.tstsx// [...]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.tsxtsx// [...]export function App() {const [queryClient] = useState(() => new QueryClient());const [billingClient] = useState(() => createBillingClient());const [accountClient] = useState(() => createAccountClient());return (<QueryClientProvider client={queryClient}><BillingProvidertrpcClient={billingClient}queryClient={queryClient}keyPrefix="billing"><AccountProvidertrpcClient={accountClient}queryClient={queryClient}keyPrefix="account">{/* ... */}</AccountProvider></BillingProvider></QueryClientProvider>);}
App.tsxtsx// [...]export function App() {const [queryClient] = useState(() => new QueryClient());const [billingClient] = useState(() => createBillingClient());const [accountClient] = useState(() => createAccountClient());return (<QueryClientProvider client={queryClient}><BillingProvidertrpcClient={billingClient}queryClient={queryClient}keyPrefix="billing"><AccountProvidertrpcClient={accountClient}queryClient={queryClient}keyPrefix="account">{/* ... */}</AccountProvider></BillingProvider></QueryClientProvider>);}
components/MyComponent.tsxtsx// [...]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.tsxtsx// [...]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 prefixesconstqueryKeys = [[['billing'], ['list'], {type : 'query' }],[['account'], ['list'], {type : 'query' }],];
tsx// Example of how the query keys look with prefixesconstqueryKeys = [[['billing'], ['list'], {type : 'query' }],[['account'], ['list'], {type : 'query' }],];
推断输入输出类型
当需要推断过程或路由器的输入输出类型时,根据场景提供两种方案:
推断整个路由器的输入输出类型
tsimport type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';import { AppRouter } from './path/to/server';export type Inputs = inferRouterInputs<AppRouter>;export type Outputs = inferRouterOutputs<AppRouter>;
tsimport type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';import { AppRouter } from './path/to/server';export type Inputs = inferRouterInputs<AppRouter>;export type Outputs = inferRouterOutputs<AppRouter>;
推断单个过程的类型
tsimport 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>;}
tsimport 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 客户端。
tsximport { useTRPCClient } from './trpc';function Component() {const trpcClient = useTRPCClient();const result = await trpcClient.path.to.procedure.query({/** input */});}
tsximport { useTRPCClient } from './trpc';function Component() {const trpcClient = useTRPCClient();const result = await trpcClient.path.to.procedure.query({/** input */});}
若采用无 React Context 的设置方案,可直接导入全局客户端实例。
tsimport { client } from './trpc';const result = await client.path.to.procedure.query({/** input */});
tsimport { client } from './trpc';const result = await client.path.to.procedure.query({/** input */});