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
呼叫您的程序。
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;}