@trpc/next
Version:
295 lines (232 loc) • 11.9 kB
Markdown
# useUtils
> Invalidate, refetch, prefetch and update cached query data with the classic trpc.useUtils() helpers
From https://trpc.io/docs/client/react/useUtils
> [!WARNING] Deprecated
> The classic React Query integration (`/react-query`) only gets bug fixes, and will be removed in the next major version of tRPC. New projects should use the [TanStack React Query integration](https://trpc.io/docs/client/tanstack-react-query) (`/tanstack-react-query`). To move an existing project, follow the [migration guide](https://trpc.io/docs/client/tanstack-react-query/migrating).
`useUtils` is a hook that gives you access to helpers that let you manage the cached data of the queries you execute via `/react-query`. These helpers are actually thin wrappers around `@tanstack/react-query`'s [`queryClient`](https://tanstack.com/query/v5/docs/reference/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.
> [!NOTE]
>
> This hook was called `useContext()` until `10.41.0` (and is still aliased for the foreseeable future)
## Usage
`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:
```ts no-check title="server.ts"
import { initTRPC } from '/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;
```
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!
```tsx no-check title="MyComponent.tsx"
import { createTRPCReact } from '/react-query';
import type { AppRouter } from './server';
const trpc = createTRPCReact<AppRouter>();
function MyComponent() {
const utils = trpc.useUtils();
utils.post.all.f;
}
```
## Helpers
These are the helpers you'll get access to via `useUtils`. The table below will help you know which tRPC helper wraps which `/react-query` helper method. Each react-query method will link to its respective docs/guide:
| tRPC helper wrapper | `/react-query` helper method |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `fetch` | [`queryClient.fetchQuery`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientfetchquery) |
| `prefetch` | [`queryClient.prefetchQuery`](https://tanstack.com/query/v5/docs/framework/react/guides/prefetching) |
| `fetchInfinite` | [`queryClient.fetchInfiniteQuery`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientfetchinfinitequery) |
| `prefetchInfinite` | [`queryClient.prefetchInfiniteQuery`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientprefetchinfinitequery) |
| `ensureData` | [`queryClient.ensureData`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientensurequerydata) |
| `invalidate` | [`queryClient.invalidateQueries`](https://tanstack.com/query/v5/docs/framework/react/guides/query-invalidation) |
| `refetch` | [`queryClient.refetchQueries`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientrefetchqueries) |
| `cancel` | [`queryClient.cancelQueries`](https://tanstack.com/query/v5/docs/framework/react/guides/query-cancellation) |
| `setData` | [`queryClient.setQueryData`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientsetquerydata) |
| `setQueriesData` | [`queryClient.setQueriesData`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientsetqueriesdata) |
| `getData` | [`queryClient.getQueryData`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientgetquerydata) |
| `setInfiniteData` | [`queryClient.setInfiniteQueryData`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientsetquerydata) |
| `getInfiniteData` | [`queryClient.getInfiniteData`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientgetquerydata) |
| `setMutationDefaults` | [`queryClient.setMutationDefaults`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientsetmutationdefaults) |
| `getMutationDefaults` | [`queryClient.getMutationDefaults`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientgetmutationdefaults) |
| `isMutating` | [`queryClient.isMutating`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientismutating) |
| `reset` | [`queryClient.resetQueries`](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientresetqueries) |
### ❓ The function I want isn't here!
`/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](https://github.com/trpc/trpc/issues/new/choose) requesting it.
In the meantime, you can import and use the function directly from `/react-query`. We also provide a [getQueryKey](https://trpc.io/docs/client/react/getQueryKey) which you can use to get the correct queryKey on the filters when using these functions.
## Proxy client
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 no-check
import { useState } from 'react';
import { trpc } from './utils/trpc';
function MyComponent() {
const [apiKey, setApiKey] = useState('');
const utils = trpc.useUtils();
return (
<form
onSubmit={async (event) => {
const apiKey = await utils.client.apiKey.create.mutate();
setApiKey(apiKey);
}}
>
{/* form content */}
</form>
);
}
```
## Query Invalidation
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 no-check
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 than
just one query.
#### Example code
<details>
<summary>Backend code</summary>
```tsx no-check title="server/routers/_app.ts"
import { initTRPC } from '/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' }] };
}),
}),
});
```
</details>
```tsx no-check
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 no-check
import { createTRPCReact } from '/react-query';
import type { AppRouter } from '../server';
export const trpc = createTRPCReact<AppRouter>({
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
Aside from the query helpers, the object `useUtils` returns also contains the following properties:
```ts no-check
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;
}
```