useUtils
useUtils
是一个钩子,可让你访问辅助程序,让你管理通过 @trpc/react-query
执行的查询的缓存数据。这些助手实际上是 @tanstack/react-query
的 queryClient
方法的薄封装。如果你想了解比我们此处提供的更多关于 useUtils
辅助程序的选项和使用模式的深入信息,我们将链接到它们各自的 @tanstack/react-query
文档,以便你可以相应地参考它们。
¥useUtils
is a hook that gives you access to helpers that let you manage the cached data of the queries you execute via @trpc/react-query
. These helpers are actually thin wrappers around @tanstack/react-query
's queryClient
methods. If you want more in-depth information about options and usage patterns for useUtils
helpers than what we provide here, we will link to their respective @tanstack/react-query
docs so you can refer to them accordingly.
这个钩子在 10.41.0
之前被称为 useContext()
(并且在可预见的将来仍然是别名)
¥This hook was called useContext()
until 10.41.0
(and is still aliased for the foreseeable future)
用法
¥Usage
useUtils
返回一个对象,其中包含路由中所有可用的查询。使用它的方式与 trpc
客户端对象相同。到达查询后,你将可以访问查询助手。例如,假设你有一个带有 all
查询的 post
路由:
¥useUtils
returns an object with all the available queries you have in your routers. You use it the same way as your trpc
client object. Once you reach a query, you'll have access to the query helpers. For example, let's say you have a post
router with an all
query:
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
查询时,我们将可以访问我们的查询助手!
¥Now in our component, when we navigate the object useUtils
gives us and reach the post.all
query, we'll get access to our query helpers!
MyComponent.tsxtsx
functionMyComponent () {constutils =trpc .useUtils ();utils .post .all .f ;// [...]}
MyComponent.tsxtsx
functionMyComponent () {constutils =trpc .useUtils ();utils .post .all .f ;// [...]}
辅助程序
¥Helpers
这些是你可以通过 useUtils
访问的辅助程序。下表将帮助你了解哪个 tRPC 辅助程序封装了哪个 @tanstack/react-query
辅助程序方法。每个反应查询方法将链接到其各自的文档/指南:
¥These are the helpers you'll get access to via useUtils
. The table below will help you know which tRPC helper wraps which @tanstack/react-query
helper method. Each react-query method will link to its respective docs/guide:
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 |
getData | queryClient.getQueryData |
setInfiniteData | queryClient.setInfiniteQueryData |
getInfiniteData | queryClient.getInfiniteData |
❓ 我想要的功能不在这里!
¥❓ The function I want isn't here!
@tanstack/react-query
有很多函数我们还没有放入 tRPC 上下文中。如果你需要此处没有的功能,请随时向 打开功能请求 请求。
¥@tanstack/react-query
has a lot of functions that we haven't put in the tRPC context yet. If you need a function that isn't here, feel free to open a feature request requesting it.
同时,你可以直接从 @tanstack/react-query
导入并使用该函数。我们还提供了 getQueryKey,你可以在使用这些函数时使用它来获取过滤器上的正确 queryKey。
¥In the meantime, you can import and use the function directly from @tanstack/react-query
. We also provide a getQueryKey which you can use to get the correct queryKey on the filters when using these functions.
代理客户端
¥Proxy client
除了上面的 react-query helpers 之外,上下文还公开了你的 tRPC 代理客户端。这使你可以使用 async
/await
调用过程,而无需创建额外的普通客户端。
¥In addition to the above react-query helpers, the context also exposes your tRPC proxy client. This lets you call your procedures with async
/await
without needing to create an additional vanilla client.
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>);}
查询失效
¥Query Invalidation
你可以通过 invalidate
辅助程序使查询无效。invalidate
实际上是一个特殊的辅助程序,因为与其他辅助程序不同,它在路由映射的每个级别都可用。这意味着你可以在单个查询、整个路由或每个路由上运行 invalidate
(如果你愿意)。我们将在下面的部分中了解更多详细信息。
¥You invalidate queries via the invalidate
helper. invalidate
is actually a special helper given that, unlike the other helpers, it's available at every level of the router map. This means you can either run invalidate
on a single query, a whole router, or every router if you want. We get more in detail in the sections below.
使单个查询无效
¥Invalidating a single query
你可以使与单个过程相关的查询无效,甚至可以根据传递给它的输入进行过滤,以防止对后端进行不必要的调用。
¥You can invalidate a query relating to a single procedure and even filter based on the input passed to it to prevent unnecessary calls to the back end.
示例代码
¥Example code
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 👍},});// [...]}
在整个路由上失效
¥Invalidating across whole routers
还可以使整个路由上的查询无效,而不仅仅是一个查询。
¥It is also possible to invalidate queries across an entire router rather then just one query.
示例代码
¥Example code
Backend code
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️⃣!// [...]}
使每个突变的完整缓存无效
¥Invalidate full cache on every mutation
准确跟踪突变应该使哪些查询无效是很困难的,因此,作为任何突变的副作用,使整个缓存无效可能是一种务实的解决方案。由于我们有请求批处理,因此这种失效只会在一个请求中重新获取你正在查看的页面上的所有查询。
¥Keeping track of exactly what queries a mutation should invalidate is hard, therefore, it can be a pragmatic solution to invalidate the full cache as a side-effect on any mutation. Since we have request batching, this invalidation will simply refetch all queries on the page you're looking at in one single request.
我们添加了一项功能来帮助解决此问题:
¥We have added a feature to help with this:
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();},},},});
其他选项
¥Additional Options
除了查询助手之外,useUtils
返回的对象还包含以下属性:
¥Aside from the query helpers, the object useUtils
returns also contains the following properties:
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;}