# TanStack Query

> Query, mutation and subscription options for TanStack Query, from one package and its React adapter.

`@trpc12/tanstack-query` turns a client into plain TanStack options objects (`queryOptions()`, `mutationOptions()`, keys and filters), so it works with every TanStack adapter. `@trpc12/tanstack-react-query` re-exports it and adds a way to provide the client to components, and a subscription primitive:

| Framework                          | Package                        | Provide the client            | Subscribe         |
| ---------------------------------- | ------------------------------ | ----------------------------- | ----------------- |
| [React](https://v12.trpc.io/v12/docs/integrations/tanstack-query/react.md) | `@trpc12/tanstack-react-query` | `<TrpcProvider>`, `useTrpc()` | `useSubscription` |

Install the core with your framework's package and TanStack's adapter, for React:

```package-install
@trpc12/trpc effect @trpc12/tanstack-react-query @tanstack/react-query
```

## The helpers

`createTrpcQueryUtils({ client })` mirrors the client's router: each procedure gets the helpers for its type, and every router and procedure gets `pathKey()` and `pathFilter()`, which match everything under it.

```ts
import { createTrpcQueryUtils } from '@trpc12/tanstack-query';

export const trpc = createTrpcQueryUtils({ client });

useQuery(trpc.post.byId.queryOptions({ input: { id } }));
useInfiniteQuery(
  trpc.post.list.infiniteOptions({
    input: (cursor: string | undefined) => ({ limit: 10, cursor }),
    initialPageParam: undefined,
    getNextPageParam: (last) => last.nextCursor,
  }),
);
useMutation(trpc.post.create.mutationOptions());
await queryClient.invalidateQueries(trpc.post.pathFilter());
useQueries({
  queries: ids.map((id) => trpc.post.byId.queryOptions({ input: { id } })),
});
useMutationState({
  filters: trpc.post.create.mutationFilter({ status: 'pending' }),
  select: (mutation) => mutation.state.variables,
});
```

| Procedure    | Helpers                                                                                                                                                                        |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| query        | `queryOptions()`, `queryKey()`, `queryFilter()`, `infiniteOptions()`, `infiniteKey()`                                                                                          |
| mutation     | `mutationOptions()`, `mutationKey()`, `mutationFilter()`, and for a streamed output `streamedMutationOptions()` ([Streamed mutations](https://v12.trpc.io/v12/docs/integrations/tanstack-query/streamed-mutations.md)) |
| subscription | `subscriptionOptions()`, `liveOptions()`, `streamedOptions()` ([Subscriptions](https://v12.trpc.io/v12/docs/integrations/tanstack-query/subscriptions.md))                                                             |

`input: skipToken` disables a query or a subscription. The options take TanStack's own options next to `input` (`staleTime`, `select`, `onSuccess`, …), and their types follow the procedure's output and errors.

## Call options

Pass what links read in `context`, kept apart from TanStack's own options: `trpc.post.byId.queryOptions({ input, context: { dedupe: false } })` ([Call options](https://v12.trpc.io/v12/docs/client/overview.md#call-options)).

## Cancellation

Queries and infinite queries pass TanStack's `signal` to the call, so TanStack [cancels](https://tanstack.com/query/latest/docs/framework/react/guides/query-cancellation) a fetch that nothing observes any more (the component unmounted, or the input changed) and the server's `signal` aborts too. `queryClient.cancelQueries()` does the same. A cancelled query goes back to its previous state rather than caching a late result. This was v11's `abortOnUnmount: true`.

## Structural sharing

A refetch that returns equal data keeps the old reference, `Date`s, `Map`s and `Set`s included, so it doesn't re-render. `queryOptions()` sets `structuralSharing` for that; it's exported for other queries.

## Credits

The Vue, Solid, Svelte and Angular adapters were built to match [oRPC's TanStack Query integration](https://orpc.dev/docs/integrations/tanstack-query).
