useUtils
이 페이지는 PageTurner AI로 번역되었습니다(베타). 프로젝트 공식 승인을 받지 않았습니다. 오류를 발견하셨나요? 문제 신고 →
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.tsxtsxfunctionMyComponent () {constutils =trpc .useUtils ();utils .post .all .f ;// [...]}
MyComponent.tsxtsxfunctionMyComponent () {constutils =trpc .useUtils ();utils .post .all .f ;// [...]}
헬퍼
다음은 useUtils를 통해 접근할 수 있는 헬퍼 목록입니다. 아래 표는 tRPC 헬퍼가 어떤 @tanstack/react-query 헬퍼 메서드를 래핑하는지 보여줍니다. 각 react-query 메서드는 해당 문서/가이드로 연결됩니다:
| tRPC helper wrapper | @tanstack/react-query helper method |
|---|---|
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 |
setMutationDefaults | queryClient.setMutationDefaults |
getMutationDefaults | queryClient.getMutationDefaults |
isMutating | queryClient.isMutating |
❓ 원하는 함수가 여기에 없습니다!
@tanstack/react-query에는 아직 tRPC 컨텍스트에 추가하지 않은 많은 함수가 있습니다. 여기에 없는 함수가 필요하시다면, 기능 요청을 열어 제안해 주세요.
그동안 @tanstack/react-query에서 직접 함수를 가져와 사용할 수 있습니다. 또한 이러한 함수를 사용할 때 필터에 올바른 queryKey를 가져오는 데 사용할 수 있는 getQueryKey도 제공합니다.
프록시 클라이언트
위의 react-query 헬퍼 외에도, 컨텍스트는 tRPC 프록시 클라이언트도 노출합니다. 이를 통해 추가적인 일반 클라이언트를 생성하지 않고도 async/await로 프로시저를 호출할 수 있습니다.
tsximport { 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>);}
tsximport { 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를 실행할 수 있습니다. 아래 섹션에서 자세히 설명하겠습니다.
단일 쿼리 무효화
단일 프로시저와 관련된 쿼리를 무효화할 수 있으며, 전달된 입력을 기반으로 필터링하여 백엔드에 불필요한 호출을 방지할 수도 있습니다.
예제 코드
tsximport { 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 👍},});// [...]}
tsximport { 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 👍},});// [...]}
라우터 전체 무효화
단일 쿼리가 아닌 전체 라우터에 걸쳐 쿼리를 무효화할 수도 있습니다.
예제 코드
Backend code
server/routers/_app.tstsximport { 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.tstsximport { 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' }] };}),}),});
tsximport { 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️⃣!// [...]}
tsximport { 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️⃣!// [...]}
모든 뮤테이션에서 전체 캐시 무효화
뮤테이션이 어떤 쿼리를 무효화해야 하는지 정확히 추적하기 어렵기 때문에, 모든 뮤테이션의 부수 효과로 _전체 캐시_를 무효화하는 것이 실용적인 해결책이 될 수 있습니다. 요청 배칭이 지원되므로, 이 무효화 작업은 현재 보고 있는 페이지의 모든 쿼리를 단일 요청으로 재조회합니다.
이를 돕기 위해 다음 기능을 추가했습니다:
tsexport 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();},},},});
tsexport 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가 반환하는 객체에는 다음 속성들이 포함됩니다:
tsinterface 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;}
tsinterface 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;}