Aller au contenu principal
Version : 11.x

TanStack React Query

Traduction Bêta Non Officielle

Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →

Exemple rapide de requête

tsx
import { useQuery } from '@tanstack/react-query';
import { useTRPC } from './trpc';
 
function Users() {
const trpc = useTRPC();
 
const greetingQuery = useQuery(trpc.greeting.queryOptions({ name: 'Jerry' }));
 
// greetingQuery.data === 'Hello Jerry'
}
tsx
import { useQuery } from '@tanstack/react-query';
import { useTRPC } from './trpc';
 
function Users() {
const trpc = useTRPC();
 
const greetingQuery = useQuery(trpc.greeting.queryOptions({ name: 'Jerry' }));
 
// greetingQuery.data === 'Hello Jerry'
}

Utilisation

La philosophie de ce client est de fournir des factories minces et typées qui fonctionnent nativement et de manière type-safe avec Tanstack React Query. Cela signifie qu'en suivant simplement les autocomplétions fournies par le client, vous pouvez vous concentrer sur le développement en vous appuyant uniquement sur les connaissances des docs TanStack React Query.

tsx
export default function Basics() {
const trpc = useTRPC();
const queryClient = useQueryClient();
 
// Create QueryOptions which can be passed to query hooks
const myQueryOptions = trpc.path.to.query.queryOptions({ /** inputs */ })
const myQuery = useQuery(myQueryOptions)
// or:
// useSuspenseQuery(myQueryOptions)
// useInfiniteQuery(myQueryOptions)
 
// Create MutationOptions which can be passed to useMutation
const myMutationOptions = trpc.path.to.mutation.mutationOptions()
const myMutation = useMutation(myMutationOptions)
 
// Create a QueryKey which can be used to manipulate many methods
// on TanStack's QueryClient in a type-safe manner
const myQueryKey = trpc.path.to.query.queryKey()
 
const invalidateMyQueryKey = () => {
queryClient.invalidateQueries({ queryKey: myQueryKey })
}
 
return (
// Your app here
null
)
}
tsx
export default function Basics() {
const trpc = useTRPC();
const queryClient = useQueryClient();
 
// Create QueryOptions which can be passed to query hooks
const myQueryOptions = trpc.path.to.query.queryOptions({ /** inputs */ })
const myQuery = useQuery(myQueryOptions)
// or:
// useSuspenseQuery(myQueryOptions)
// useInfiniteQuery(myQueryOptions)
 
// Create MutationOptions which can be passed to useMutation
const myMutationOptions = trpc.path.to.mutation.mutationOptions()
const myMutation = useMutation(myMutationOptions)
 
// Create a QueryKey which can be used to manipulate many methods
// on TanStack's QueryClient in a type-safe manner
const myQueryKey = trpc.path.to.query.queryKey()
 
const invalidateMyQueryKey = () => {
queryClient.invalidateQueries({ queryKey: myQueryKey })
}
 
return (
// Your app here
null
)
}

L'objet trpc est entièrement type-safe et fournira des autocomplétions pour toutes les procédures de votre AppRouter. En fin de chaîne de proxy, les méthodes suivantes sont disponibles :

queryOptions - interrogation de données

Disponible pour toutes les procédures de requête. Fournit un wrapper type-safe autour de la fonction queryOptions de Tanstack. Le premier argument correspond à l'entrée de la procédure, et le second accepte toutes les options natives de Tanstack React Query.

ts
const queryOptions = trpc.path.to.query.queryOptions(
{
/** input */
id: 'foo',
},
{
// Any Tanstack React Query options
staleTime: 1000,
},
);
ts
const queryOptions = trpc.path.to.query.queryOptions(
{
/** input */
id: 'foo',
},
{
// Any Tanstack React Query options
staleTime: 1000,
},
);

Vous pouvez également fournir un objet trpc à la fonction queryOptions pour transmettre des options de requête tRPC au client.

ts
const queryOptions = trpc.path.to.query.queryOptions(
{
/** input */
id: 'foo',
},
{
trpc: {
// Provide tRPC request options to the client
context: {
// see https://trpc.io/docs/client/links#managing-context
},
},
},
);
ts
const queryOptions = trpc.path.to.query.queryOptions(
{
/** input */
id: 'foo',
},
{
trpc: {
// Provide tRPC request options to the client
context: {
// see https://trpc.io/docs/client/links#managing-context
},
},
},
);

Si vous souhaitez désactiver une requête de manière type-safe, vous pouvez utiliser skipToken :

ts
const query = useQuery(
trpc.user.details.queryOptions(
user?.id && project?.id
? {
userId: user.id,
projectId: project.id,
}
: skipToken,
{
staleTime: 1000,
},
),
);
ts
const query = useQuery(
trpc.user.details.queryOptions(
user?.id && project?.id
? {
userId: user.id,
projectId: project.id,
}
: skipToken,
{
staleTime: 1000,
},
),
);

Le résultat peut être passé aux hooks useQuery ou useSuspenseQuery, ou aux méthodes du client de requête comme fetchQuery, prefetchQuery, prefetchInfiniteQuery, invalidateQueries, etc.

infiniteQueryOptions - interrogation de données infinies

Disponible pour toutes les procédures de requête qui acceptent une entrée de curseur. Fournit un wrapper type-safe autour de la fonction infiniteQueryOptions de Tanstack. Le premier argument correspond à l'entrée de la procédure, et le second accepte toutes les options natives de Tanstack React Query.

ts
const infiniteQueryOptions = trpc.path.to.query.infiniteQueryOptions(
{
/** input */
},
{
// Any Tanstack React Query options
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
},
);
ts
const infiniteQueryOptions = trpc.path.to.query.infiniteQueryOptions(
{
/** input */
},
{
// Any Tanstack React Query options
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
},
);

queryKey - obtention de la clé de requête et opérations sur le client de requête

Disponible pour toutes les procédures de requête. Permet d'accéder à la clé de requête de manière type-safe.

ts
const queryKey = trpc.path.to.query.queryKey();
ts
const queryKey = trpc.path.to.query.queryKey();

Comme Tanstack React Query utilise un matching flou pour les clés de requête, vous pouvez également créer une clé de requête partielle pour tout sous-chemin afin de correspondre à toutes les requêtes d'un routeur :

ts
const queryKey = trpc.router.pathKey();
ts
const queryKey = trpc.router.pathKey();

Ou même le chemin racine pour correspondre à toutes les requêtes tRPC :

ts
const queryKey = trpc.pathKey();
ts
const queryKey = trpc.pathKey();

infiniteQueryKey - obtention de la clé de requête infinie

Disponible pour toutes les procédures de requête qui acceptent une entrée de curseur. Permet d'accéder à la clé de requête pour une requête infinie de manière type-safe.

ts
const infiniteQueryKey = trpc.path.to.query.infiniteQueryKey({
/** input */
});
ts
const infiniteQueryKey = trpc.path.to.query.infiniteQueryKey({
/** input */
});

Le résultat peut être utilisé avec des méthodes du client de requête telles que getQueryData, setQueryData, invalidateQueries, etc.

ts
// Get cached data for an infinite query
const cachedData = queryClient.getQueryData(
trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),
);
 
// Set cached data for an infinite query
queryClient.setQueryData(
trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),
(data) => {
// Modify the data
return data;
},
);
ts
// Get cached data for an infinite query
const cachedData = queryClient.getQueryData(
trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),
);
 
// Set cached data for an infinite query
queryClient.setQueryData(
trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),
(data) => {
// Modify the data
return data;
},
);

queryFilter - création de filtres de requête

Disponible pour toutes les procédures de requête. Permet de créer des filtres de requête de manière type-safe.

ts
const queryFilter = trpc.path.to.query.queryFilter(
{
/** input */
},
{
// Any Tanstack React Query filter
predicate: (query) => {
return !!query.state.data;
},
},
);
ts
const queryFilter = trpc.path.to.query.queryFilter(
{
/** input */
},
{
// Any Tanstack React Query filter
predicate: (query) => {
return !!query.state.data;
},
},
);

Comme pour les clés de requête, si vous souhaitez appliquer un filtre sur un routeur entier, vous pouvez utiliser pathFilter pour cibler n'importe quel sous-chemin.

ts
const queryFilter = trpc.path.pathFilter({
// Any Tanstack React Query filter
predicate: (query) => {
return !!query.state.data;
},
});
ts
const queryFilter = trpc.path.pathFilter({
// Any Tanstack React Query filter
predicate: (query) => {
return !!query.state.data;
},
});

Utile pour créer des filtres pouvant être passés aux méthodes client comme queryClient.invalidateQueries, etc.

infiniteQueryFilter - création de filtres de requête infinie

Disponible pour toutes les procédures de requête qui acceptent une entrée de curseur. Permet de créer des filtres de requête pour les requêtes infinies de manière type-safe.

ts
const infiniteQueryFilter = trpc.path.to.query.infiniteQueryFilter(
{
/** input */
},
{
// Any Tanstack React Query filter
predicate: (query) => {
return !!query.state.data;
},
},
);
ts
const infiniteQueryFilter = trpc.path.to.query.infiniteQueryFilter(
{
/** input */
},
{
// Any Tanstack React Query filter
predicate: (query) => {
return !!query.state.data;
},
},
);

Utile pour créer des filtres pouvant être passés aux méthodes client comme queryClient.invalidateQueries, etc.

ts
await queryClient.invalidateQueries(
trpc.path.to.query.infiniteQueryFilter(
{},
{
predicate: (query) => {
// Filter logic based on query state
return query.state.status === 'success';
},
},
),
);
ts
await queryClient.invalidateQueries(
trpc.path.to.query.infiniteQueryFilter(
{},
{
predicate: (query) => {
// Filter logic based on query state
return query.state.status === 'success';
},
},
),
);

mutationOptions - création d'options de mutation

Disponible pour toutes les procédures de mutation. Fournit une fonction d'identité type-safe pour construire des options pouvant être passées à useMutation.

ts
const mutationOptions = trpc.path.to.mutation.mutationOptions({
// Any Tanstack React Query options
onSuccess: (data) => {
// do something with the data
},
});
ts
const mutationOptions = trpc.path.to.mutation.mutationOptions({
// Any Tanstack React Query options
onSuccess: (data) => {
// do something with the data
},
});

mutationKey - obtention de la clé de mutation

Disponible pour toutes les procédures de mutation. Permet d'obtenir la clé de mutation de manière type-safe.

ts
const mutationKey = trpc.path.to.mutation.mutationKey();
ts
const mutationKey = trpc.path.to.mutation.mutationKey();

subscriptionOptions - création d'options d'abonnement

TanStack ne fournit pas de hook d'abonnement, nous exposons donc notre propre abstraction qui fonctionne avec une configuration standard d'abonnement tRPC. Disponible pour toutes les procédures d'abonnement. Fournit une fonction d'identité type-safe pour construire des options pouvant être passées à useSubscription. Notez que vous devez avoir configuré soit le httpSubscriptionLink soit le wsLink dans votre client tRPC pour utiliser les abonnements.

tsx
function SubscriptionExample() {
const trpc = useTRPC();
const subscription = useSubscription(
trpc.path.to.subscription.subscriptionOptions(
{
/** input */
},
{
enabled: true,
onStarted: () => {
// do something when the subscription is started
},
onData: (data) => {
// you can handle the data here
},
onError: (error) => {
// you can handle the error here
},
onConnectionStateChange: (state) => {
// you can handle the connection state here
},
},
),
);
 
// Or you can handle the state here
subscription.data; // The lastly received data
subscription.error; // The lastly received error
 
/**
* The current status of the subscription.
* Will be one of: `'idle'`, `'connecting'`, `'pending'`, or `'error'`.
*
* - `idle`: subscription is disabled or ended
* - `connecting`: trying to establish a connection
* - `pending`: connected to the server, receiving data
* - `error`: an error occurred and the subscription is stopped
*/
subscription.status;
 
// Reset the subscription (if you have an error etc)
subscription.reset();
 
return <>{/* ... */}</>;
}
tsx
function SubscriptionExample() {
const trpc = useTRPC();
const subscription = useSubscription(
trpc.path.to.subscription.subscriptionOptions(
{
/** input */
},
{
enabled: true,
onStarted: () => {
// do something when the subscription is started
},
onData: (data) => {
// you can handle the data here
},
onError: (error) => {
// you can handle the error here
},
onConnectionStateChange: (state) => {
// you can handle the connection state here
},
},
),
);
 
// Or you can handle the state here
subscription.data; // The lastly received data
subscription.error; // The lastly received error
 
/**
* The current status of the subscription.
* Will be one of: `'idle'`, `'connecting'`, `'pending'`, or `'error'`.
*
* - `idle`: subscription is disabled or ended
* - `connecting`: trying to establish a connection
* - `pending`: connected to the server, receiving data
* - `error`: an error occurred and the subscription is stopped
*/
subscription.status;
 
// Reset the subscription (if you have an error etc)
subscription.reset();
 
return <>{/* ... */}</>;
}

Préfixage des clés de requête

Lorsque vous utilisez plusieurs clients tRPC dans une même application (par ex. pour vous connecter à différents services backend), les requêtes ayant le même chemin entreront en conflit dans le cache. Vous pouvez éviter cela en activant le préfixage des clés de requête.

tsx
// Without prefixes - these would collide!
const authQuery = useQuery(trpcAuth.list.queryOptions()); // auth service
const billingQuery = useQuery(trpcBilling.list.queryOptions()); // billing service
tsx
// Without prefixes - these would collide!
const authQuery = useQuery(trpcAuth.list.queryOptions()); // auth service
const billingQuery = useQuery(trpcBilling.list.queryOptions()); // billing service

Activez le feature flag lors de la création de votre contexte :

utils/trpc.ts
tsx
// [...]
 
const billing = createTRPCContext<BillingRouter, { keyPrefix: true }>();
export const BillingProvider = billing.TRPCProvider;
export const useBilling = billing.useTRPC;
export const createBillingClient = () =>
createTRPCClient<BillingRouter>({
links: [
/* ... */
],
});
 
const account = createTRPCContext<AccountRouter, { keyPrefix: true }>();
export const AccountProvider = account.TRPCProvider;
export const useAccount = account.useTRPC;
export const createAccountClient = () =>
createTRPCClient<AccountRouter>({
links: [
/* ... */
],
});
utils/trpc.ts
tsx
// [...]
 
const billing = createTRPCContext<BillingRouter, { keyPrefix: true }>();
export const BillingProvider = billing.TRPCProvider;
export const useBilling = billing.useTRPC;
export const createBillingClient = () =>
createTRPCClient<BillingRouter>({
links: [
/* ... */
],
});
 
const account = createTRPCContext<AccountRouter, { keyPrefix: true }>();
export const AccountProvider = account.TRPCProvider;
export const useAccount = account.useTRPC;
export const createAccountClient = () =>
createTRPCClient<AccountRouter>({
links: [
/* ... */
],
});
App.tsx
tsx
import { useState } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import {
BillingProvider,
AccountProvider,
createBillingClient,
createAccountClient,
} from './utils/trpc';
 
// [...]
 
export function App() {
const [queryClient] = useState(() => new QueryClient());
const [billingClient] = useState(() => createBillingClient());
const [accountClient] = useState(() => createAccountClient());
 
return (
<QueryClientProvider client={queryClient}>
<BillingProvider
trpcClient={billingClient}
queryClient={queryClient}
keyPrefix="billing"
>
<AccountProvider
trpcClient={accountClient}
queryClient={queryClient}
keyPrefix="account"
>
<div>{/* ... */}</div>
</AccountProvider>
</BillingProvider>
</QueryClientProvider>
);
}
App.tsx
tsx
import { useState } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import {
BillingProvider,
AccountProvider,
createBillingClient,
createAccountClient,
} from './utils/trpc';
 
// [...]
 
export function App() {
const [queryClient] = useState(() => new QueryClient());
const [billingClient] = useState(() => createBillingClient());
const [accountClient] = useState(() => createAccountClient());
 
return (
<QueryClientProvider client={queryClient}>
<BillingProvider
trpcClient={billingClient}
queryClient={queryClient}
keyPrefix="billing"
>
<AccountProvider
trpcClient={accountClient}
queryClient={queryClient}
keyPrefix="account"
>
<div>{/* ... */}</div>
</AccountProvider>
</BillingProvider>
</QueryClientProvider>
);
}
components/MyComponent.tsx
tsx
import { useQuery } from '@tanstack/react-query';
import { useBilling, useAccount } from '../utils/trpc';
 
// [...]
 
export function MyComponent() {
const billing = useBilling();
const account = useAccount();
 
const billingList = useQuery(billing.list.queryOptions());
const accountList = useQuery(account.list.queryOptions());
 
return (
<div>
<div>Billing: {JSON.stringify(billingList.data ?? null)}</div>
<div>Account: {JSON.stringify(accountList.data ?? null)}</div>
</div>
);
}
components/MyComponent.tsx
tsx
import { useQuery } from '@tanstack/react-query';
import { useBilling, useAccount } from '../utils/trpc';
 
// [...]
 
export function MyComponent() {
const billing = useBilling();
const account = useAccount();
 
const billingList = useQuery(billing.list.queryOptions());
const accountList = useQuery(account.list.queryOptions());
 
return (
<div>
<div>Billing: {JSON.stringify(billingList.data ?? null)}</div>
<div>Account: {JSON.stringify(accountList.data ?? null)}</div>
</div>
);
}

Les clés de requête seront correctement préfixées pour éviter les collisions :

tsx
// Example of how the query keys look with prefixes
const queryKeys = [
[['billing'], ['list'], { type: 'query' }],
[['account'], ['list'], { type: 'query' }],
];
tsx
// Example of how the query keys look with prefixes
const queryKeys = [
[['billing'], ['list'], { type: 'query' }],
[['account'], ['list'], { type: 'query' }],
];

Inférer les types d'entrée et de sortie

Lorsque vous devez inférer les types d'entrée et de sortie pour une procédure ou un routeur, deux options sont disponibles selon la situation.

Inférer les types d'entrée et de sortie d'un routeur complet

ts
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
import type { AppRouter } from './server/router';
 
export type Inputs = inferRouterInputs<AppRouter>;
export type Outputs = inferRouterOutputs<AppRouter>;
ts
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
import type { AppRouter } from './server/router';
 
export type Inputs = inferRouterInputs<AppRouter>;
export type Outputs = inferRouterOutputs<AppRouter>;

Inférer les types pour une procédure individuelle

ts
import type { inferInput, inferOutput } from '@trpc/tanstack-react-query';
 
function Component() {
const trpc = useTRPC();
 
type Input = inferInput<typeof trpc.path.to.procedure>;
type Output = inferOutput<typeof trpc.path.to.procedure>;
}
ts
import type { inferInput, inferOutput } from '@trpc/tanstack-react-query';
 
function Component() {
const trpc = useTRPC();
 
type Input = inferInput<typeof trpc.path.to.procedure>;
type Output = inferOutput<typeof trpc.path.to.procedure>;
}

Accéder au client tRPC

Si vous avez utilisé la configuration avec le contexte React, vous pouvez accéder au client tRPC à l'aide du hook useTRPCClient.

tsx
import { useTRPCClient } from './trpc';
 
async function Component() {
const trpcClient = useTRPCClient();
 
const result = await trpcClient.getUser.query({
id: '1',
});
}
tsx
import { useTRPCClient } from './trpc';
 
async function Component() {
const trpcClient = useTRPCClient();
 
const result = await trpcClient.getUser.query({
id: '1',
});
}

Si vous avez configuré sans contexte React, vous pouvez importer directement l'instance globale du client.

ts
import { client } from './trpc';
 
const result = await client.path.to.procedure.query({
/** input */
id: 'foo',
});
ts
import { client } from './trpc';
 
const result = await client.path.to.procedure.query({
/** input */
id: 'foo',
});