Skip to main content
Version: 10.x

useUtils

useUtils 是一个钩子,可让你访问辅助程序,让你管理通过 @trpc/react-query 执行的查询的缓存数据。这些助手实际上是 @tanstack/react-queryqueryClient 方法的薄封装。如果你想了解比我们此处提供的更多关于 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.ts
ts
// @filename: server.ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
 
const t = initTRPC.create();
 
const appRouter = t.router({
post: t.router({
all: t.procedure.query(() => {
return {
posts: [
{ id: 1, title: 'everlong' },
{ id: 2, title: 'After Dark' },
],
};
}),
}),
});
 
export type AppRouter = typeof appRouter;
server.ts
ts
// @filename: server.ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
 
const t = initTRPC.create();
 
const appRouter = t.router({
post: t.router({
all: t.procedure.query(() => {
return {
posts: [
{ id: 1, title: 'everlong' },
{ id: 2, title: 'After Dark' },
],
};
}),
}),
});
 
export type AppRouter = typeof appRouter;

现在在我们的组件中,当我们导航 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.tsx
tsx
function MyComponent() {
const utils = trpc.useUtils();
utils.post.all.f;
                  
// [...]
}
MyComponent.tsx
tsx
function MyComponent() {
const utils = 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 辅助方法
fetchqueryClient.fetchQuery
prefetchqueryClient.prefetchQuery
fetchInfinitequeryClient.fetchInfiniteQuery
prefetchInfinitequeryClient.prefetchInfiniteQuery
ensureDataqueryClient.ensureData
invalidatequeryClient.invalidateQueries
refetchqueryClient.refetchQueries
cancelqueryClient.cancelQueries
setDataqueryClient.setQueryData
getDataqueryClient.getQueryData
setInfiniteDataqueryClient.setInfiniteQueryData
getInfiniteDataqueryClient.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 (
<Form
handleSubmit={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 (
<Form
handleSubmit={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.ts
tsx
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
export const t = initTRPC.create();
export const appRouter = t.router({
// sub Post router
post: 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 router
user: t.router({
all: t.procedure.query(() => {
return { users: [{ name: 'Dave Grohl' }, { name: 'Haruki Murakami' }] };
}),
}),
});
server/routers/_app.ts
tsx
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
export const t = initTRPC.create();
export const appRouter = t.router({
// sub Post router
post: 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 router
user: 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 queries
trpc.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 queries
trpc.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;
}