useUtils
useUtils
是一个钩子,它允许您访问帮助程序,这些帮助程序可以让您管理通过 @trpc/react-query
执行的查询的缓存数据。这些帮助程序实际上是围绕 @tanstack/react-query
的 queryClient
方法的薄包装器。如果您想更深入地了解 useContext
帮助程序的选项和使用模式,而不是我们在此提供的模式,我们将链接到它们各自的 @tanstack/react-query
文档,以便您可以相应地参考它们。
此钩子在 10.41.0
之前称为 useContext()
(并且在可预见的将来仍然是别名)
用法
useUtils
返回一个包含您在路由器中所有可用查询的对象。您使用它的方式与您的 trpc
客户端对象相同。到达查询后,您将可以访问查询帮助程序。例如,假设您有一个带有 all
查询的 post
路由器
server.tsts
// @filename: server.tsimport {initTRPC } from '@trpc/server';import {z } from 'zod';constt =initTRPC .create ();constappRouter =t .router ({post :t .router ({all :t .procedure .query (() => {return {posts : [{id : 1,title : 'everlong' },{id : 2,title : 'After Dark' },],};}),}),});export typeAppRouter = typeofappRouter ;
server.tsts
// @filename: server.tsimport {initTRPC } from '@trpc/server';import {z } from 'zod';constt =initTRPC .create ();constappRouter =t .router ({post :t .router ({all :t .procedure .query (() => {return {posts : [{id : 1,title : 'everlong' },{id : 2,title : 'After Dark' },],};}),}),});export typeAppRouter = typeofappRouter ;
现在在我们的组件中,当我们导航 useUtils
给我们的对象并到达 post.all
查询时,我们将可以访问我们的查询帮助程序!
MyComponent.tsxtsx
functionMyComponent () {constutils =trpc .useUtils ();utils .post .all .f ;// [...]}
MyComponent.tsxtsx
functionMyComponent () {constutils =trpc .useUtils ();utils .post .all .f ;// [...]}
帮助程序
这些是您将通过 useUtils
访问的帮助程序。下表将帮助您了解哪个 tRPC 帮助程序包装了哪个 @tanstack/react-query
帮助程序方法。每个 react-query 方法都将链接到其各自的文档/指南
tRPC 帮助程序包装器 | @tanstack/react-query 帮助程序方法 |
---|---|
fetch | queryClient.fetchQuery |
prefetch | queryClient.prefetchQuery |
fetchInfinite | queryClient.fetchInfiniteQuery |
prefetchInfinite | queryClient.prefetchInfiniteQuery |
ensureData | queryClient.ensureData |
invalidate | queryClient.invalidateQueries |
refetch | queryClient.refetchQueries |
cancel | queryClient.cancelQueries |
setData | queryClient.setQueryData |
setQueriesData | queryClient.setQueriesData |
getData | queryClient.getQueryData |
setInfiniteData | queryClient.setInfiniteQueryData |
getInfiniteData | queryClient.getInfiniteData |
❓ 我想要的函数不在这里!
@tanstack/react-query
有很多我们还没有放在 tRPC 上下文中的函数。如果您需要一个不在此处的函数,请随时 打开一个功能请求 请求它。
同时,您可以直接从 @tanstack/react-query
导入和使用该函数。我们还提供了一个 getQueryKey,您可以使用它在使用这些函数时获取过滤器上的正确 queryKey。
代理客户端
除了上述 react-query 帮助程序之外,上下文还公开了您的 tRPC 代理客户端。这使您能够使用 async
/await
调用您的过程,而无需创建额外的 vanilla 客户端。
tsx
import { trpc } from '../utils/trpc';function MyComponent() {const [apiKey, setApiKey] = useState();const utils = trpc.useUtils();return (<FormhandleSubmit={async (event) => {const apiKey = await utils.client.apiKey.create.mutate(event);setApiKey(apiKey);}}>...</Form>);}
tsx
import { trpc } from '../utils/trpc';function MyComponent() {const [apiKey, setApiKey] = useState();const utils = trpc.useUtils();return (<FormhandleSubmit={async (event) => {const apiKey = await utils.client.apiKey.create.mutate(event);setApiKey(apiKey);}}>...</Form>);}
查询失效
您可以通过 invalidate
帮助程序使查询失效。invalidate
实际上是一个特殊的帮助程序,因为它与其他帮助程序不同,它在路由器映射的每个级别都可用。这意味着您可以对单个查询、整个路由器或所有路由器运行 invalidate
(如果您愿意)。我们将在下面的部分中更详细地介绍。
使单个查询失效
您可以使与单个过程相关的查询失效,甚至可以根据传递给它的输入进行过滤,以防止对后端进行不必要的调用。
示例代码
tsx
import { trpc } from '../utils/trpc';function MyComponent() {const utils = trpc.useUtils();const mutation = trpc.post.edit.useMutation({onSuccess(input) {utils.post.all.invalidate();utils.post.byId.invalidate({ id: input.id }); // Will not invalidate queries for other id's 👍},});// [...]}
tsx
import { trpc } from '../utils/trpc';function MyComponent() {const utils = trpc.useUtils();const mutation = trpc.post.edit.useMutation({onSuccess(input) {utils.post.all.invalidate();utils.post.byId.invalidate({ id: input.id }); // Will not invalidate queries for other id's 👍},});// [...]}
在整个路由器中使查询失效
也可以使整个路由器中的查询失效,而不仅仅是一个查询。
示例代码
后端代码
server/routers/_app.tstsx
import { initTRPC } from '@trpc/server';import { z } from 'zod';export const t = initTRPC.create();export const appRouter = t.router({// sub Post routerpost: t.router({all: t.procedure.query(() => {return {posts: [{ id: 1, title: 'everlong' },{ id: 2, title: 'After Dark' },],};}),byId: t.procedure.input(z.object({id: z.string(),}),).query(({ input }) => {return {post: { id: input?.id, title: 'Look me up!' },};}),edit: t.procedure.input(z.object({ id: z.number(), title: z.string() })).mutation(({ input }) => {return { post: { id: input.id, title: input.title } };}),}),// separate user routeruser: t.router({all: t.procedure.query(() => {return { users: [{ name: 'Dave Grohl' }, { name: 'Haruki Murakami' }] };}),}),});
server/routers/_app.tstsx
import { initTRPC } from '@trpc/server';import { z } from 'zod';export const t = initTRPC.create();export const appRouter = t.router({// sub Post routerpost: t.router({all: t.procedure.query(() => {return {posts: [{ id: 1, title: 'everlong' },{ id: 2, title: 'After Dark' },],};}),byId: t.procedure.input(z.object({id: z.string(),}),).query(({ input }) => {return {post: { id: input?.id, title: 'Look me up!' },};}),edit: t.procedure.input(z.object({ id: z.number(), title: z.string() })).mutation(({ input }) => {return { post: { id: input.id, title: input.title } };}),}),// separate user routeruser: t.router({all: t.procedure.query(() => {return { users: [{ name: 'Dave Grohl' }, { name: 'Haruki Murakami' }] };}),}),});
tsx
import { trpc } from '../utils/trpc';function MyComponent() {const utils = trpc.useUtils();const invalidateAllQueriesAcrossAllRouters = () => {// 1️⃣// All queries on all routers will be invalidated 🔥utils.invalidate();};const invalidateAllPostQueries = () => {// 2️⃣// All post queries will be invalidated 📭utils.post.invalidate();};const invalidatePostById = () => {// 3️⃣// All queries in the post router with input {id:1} invalidated 📭utils.post.byId.invalidate({ id: 1 });};// Example queriestrpc.user.all.useQuery(); // Would only be validated by 1️⃣ only.trpc.post.all.useQuery(); // Would be invalidated by 1️⃣ & 2️⃣trpc.post.byId.useQuery({ id: 1 }); // Would be invalidated by 1️⃣, 2️⃣ and 3️⃣trpc.post.byId.useQuery({ id: 2 }); // would be invalidated by 1️⃣ and 2️⃣ but NOT 3️⃣!// [...]}
tsx
import { trpc } from '../utils/trpc';function MyComponent() {const utils = trpc.useUtils();const invalidateAllQueriesAcrossAllRouters = () => {// 1️⃣// All queries on all routers will be invalidated 🔥utils.invalidate();};const invalidateAllPostQueries = () => {// 2️⃣// All post queries will be invalidated 📭utils.post.invalidate();};const invalidatePostById = () => {// 3️⃣// All queries in the post router with input {id:1} invalidated 📭utils.post.byId.invalidate({ id: 1 });};// Example queriestrpc.user.all.useQuery(); // Would only be validated by 1️⃣ only.trpc.post.all.useQuery(); // Would be invalidated by 1️⃣ & 2️⃣trpc.post.byId.useQuery({ id: 1 }); // Would be invalidated by 1️⃣, 2️⃣ and 3️⃣trpc.post.byId.useQuery({ id: 2 }); // would be invalidated by 1️⃣ and 2️⃣ but NOT 3️⃣!// [...]}
在每次变异时使整个缓存失效
准确跟踪变异应该使哪些查询失效很困难,因此,在任何变异上使整个缓存失效可能是一个务实的解决方案。由于我们有请求批处理,因此此失效将只在您查看的页面上以单个请求重新获取所有查询。
我们添加了一个功能来帮助解决这个问题
ts
export const trpc = createTRPCReact<AppRouter, SSRContext>({overrides: {useMutation: {/*** This function is called whenever a `.useMutation` succeeds**/async onSuccess(opts) {/*** @note that order here matters:* The order here allows route changes in `onSuccess` without* having a flash of content change whilst redirecting.**/// Calls the `onSuccess` defined in the `useQuery()`-options:await opts.originalFn();// Invalidate all queries in the react-query cache:await opts.queryClient.invalidateQueries();},},},});
ts
export const trpc = createTRPCReact<AppRouter, SSRContext>({overrides: {useMutation: {/*** This function is called whenever a `.useMutation` succeeds**/async onSuccess(opts) {/*** @note that order here matters:* The order here allows route changes in `onSuccess` without* having a flash of content change whilst redirecting.**/// Calls the `onSuccess` defined in the `useQuery()`-options:await opts.originalFn();// Invalidate all queries in the react-query cache:await opts.queryClient.invalidateQueries();},},},});
其他选项
除了查询帮助程序之外,useUtils
返回的对象还包含以下属性
ts
interface ProxyTRPCContextProps<TRouter extends AnyRouter, TSSRContext> {/*** The `TRPCClient`*/client: TRPCClient<TRouter>;/*** The SSR context when server-side rendering* @default null*/ssrContext?: TSSRContext | null;/*** State of SSR hydration.* - `false` if not using SSR.* - `prepass` when doing a prepass to fetch queries' data* - `mounting` before TRPCProvider has been rendered on the client* - `mounted` when the TRPCProvider has been rendered on the client* @default false*/ssrState?: SSRState;/*** Abort loading query calls when unmounting a component - usually when navigating to a new page* @default false*/abortOnUnmount?: boolean;}
ts
interface ProxyTRPCContextProps<TRouter extends AnyRouter, TSSRContext> {/*** The `TRPCClient`*/client: TRPCClient<TRouter>;/*** The SSR context when server-side rendering* @default null*/ssrContext?: TSSRContext | null;/*** State of SSR hydration.* - `false` if not using SSR.* - `prepass` when doing a prepass to fetch queries' data* - `mounting` before TRPCProvider has been rendered on the client* - `mounted` when the TRPCProvider has been rendered on the client* @default false*/ssrState?: SSRState;/*** Abort loading query calls when unmounting a component - usually when navigating to a new page* @default false*/abortOnUnmount?: boolean;}