メインコンテンツへスキップ
バージョン: 11.x

TanStack React Query

非公式ベータ版翻訳

このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →

Compared to our classic React Query Integration this client is simpler and more TanStack Query-native, providing factories for common TanStack React Query interfaces like QueryKeys, QueryOptions, and MutationOptions. We think it's the future and recommend using this over the classic client, read the announcement post for more information about this change.

クイッククエリの例

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'
}

使用方法

このクライアントの設計思想は、TanStack React Query とネイティブかつ型安全に連携する薄型の型安全ファクトリーを提供することです。つまりクライアントが提供するオートコンプリートに従うだけで、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 manipulated 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
)
}
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 manipulated 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
)
}

trpcオブジェクトは完全に型安全で、AppRouter内の全プロシージャに対してオートコンプリートを提供します。プロキシの末端では以下のメソッドが利用可能です:

queryOptions - データクエリ

全クエリプロシージャで利用可能です。TanStackのqueryOptions関数を型安全にラップします。第一引数はプロシージャへの入力、第二引数はTanStack React Queryの任意のネイティブオプションを受け付けます。

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

queryOptions関数にtrpcオブジェクトを追加で渡すことで、クライアントにtRPCリクエストオプションを提供できます。

ts
const queryOptions = trpc.path.to.query.queryOptions(
{
/** input */
},
{
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 */
},
{
trpc: {
// Provide tRPC request options to the client
context: {
// see https://trpc.io/docs/client/links#managing-context
},
},
},
);

型安全な方法でクエリを無効化したい場合、skipTokenが使用できます:

ts
import { skipToken } from '@tanstack/react-query';
const query = useQuery(
trpc.user.details.queryOptions(
user?.id && project?.id
? {
userId: user.id,
projectId: project.id,
}
: skipToken,
{
staleTime: 1000,
},
),
);
ts
import { skipToken } from '@tanstack/react-query';
const query = useQuery(
trpc.user.details.queryOptions(
user?.id && project?.id
? {
userId: user.id,
projectId: project.id,
}
: skipToken,
{
staleTime: 1000,
},
),
);

結果はuseQueryuseSuspenseQueryフック、あるいはfetchQueryprefetchQueryprefetchInfiniteQueryinvalidateQueriesなどのクエリクライアントメソッドに渡せます。

infiniteQueryOptions - 無限データクエリ

カーソル入力を取る全クエリプロシージャで利用可能です。TanStackのinfiniteQueryOptions関数を型安全にラップします。第一引数はプロシージャへの入力、第二引数は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 - クエリキーの取得とクエリクライアント操作

全クエリプロシージャで利用可能です。型安全な方法でクエリキーにアクセスできます。

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

TanStack React Queryはクエリキーにファジーマッチングを使用するため、ルーターに属する全クエリにマッチさせる任意のサブパスの部分クエリキーを作成できます:

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

全tRPCクエリにマッチさせるルートパスも指定可能です:

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

infiniteQueryKey - 無限クエリキーの取得

カーソル入力を取る全クエリプロシージャで利用可能です。型安全な方法で無限クエリのクエリキーにアクセスできます。

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

結果はgetQueryDatasetQueryDatainvalidateQueriesなどのクエリクライアントメソッドで使用できます。

ts
const queryClient = useQueryClient();
// 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
const queryClient = useQueryClient();
// 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 - クエリフィルター作成

全クエリプロシージャで利用可能です。型安全な方法でクエリフィルターを作成できます。

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

クエリキーと同様に、ルーター全体にフィルターを適用したい場合、任意のサブパスを対象とするpathFilterを使用できます。

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

queryClient.invalidateQueriesなどのクライアントメソッドに渡せるフィルター作成に有用です。

infiniteQueryFilter - 無限クエリフィルター作成

カーソル入力を取る全クエリプロシージャで利用可能です。型安全な方法で無限クエリ用のクエリフィルターを作成できます。

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

queryClient.invalidateQueriesなどのクライアントメソッドに渡せるフィルター作成に有用です。

ts
await queryClient.invalidateQueries(
trpc.path.to.query.infiniteQueryFilter(
{},
{
predicate: (query) => {
// Filter logic based on query state
return query.state.data?.pages.length > 0;
},
},
),
);
ts
await queryClient.invalidateQueries(
trpc.path.to.query.infiniteQueryFilter(
{},
{
predicate: (query) => {
// Filter logic based on query state
return query.state.data?.pages.length > 0;
},
},
),
);

mutationOptions - ミューテーションオプション作成

全ミューテーションプロシージャで利用可能です。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 - ミューテーションキー取得

全ミューテーションプロシージャで利用可能です。型安全な方法でミューテーションキーを取得できます。

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

subscriptionOptions - サブスクリプションオプション作成

TanStackはサブスクリプションフックを提供しないため、標準tRPCサブスクリプション設定と連携する独自の抽象化レイヤーを公開しています。全サブスクリプションプロシージャで利用可能です。useSubscriptionに渡せるオプションを構築する型安全な識別関数を提供します。サブスクリプションを使用するには、tRPCクライアントでhttpSubscriptionLinkまたはwsLinkのいずれかを設定する必要があります。

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 <>{/* ... */}</>;
}

クエリキープレフィックス

単一アプリケーション内で複数のtRPCプロバイダーを使用する場合(例: 異なるバックエンドサービスへの接続)、同じパスを持つクエリはキャッシュで衝突する可能性があります。クエリキープレフィックスを有効化することでこの問題を防げます。

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

コンテキスト作成時に機能フラグを有効にします:

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
// [...]
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"
>
{/* ... */}
</AccountProvider>
</BillingProvider>
</QueryClientProvider>
);
}
App.tsx
tsx
// [...]
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"
>
{/* ... */}
</AccountProvider>
</BillingProvider>
</QueryClientProvider>
);
}
components/MyComponent.tsx
tsx
// [...]
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
// [...]
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>
);
}

クエリキーには衝突を回避するため適切にプレフィックスが付与されます:

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' }],
];

入力と出力の型の推論

プロシージャやルーターの入力と出力の型を推論する必要がある場合、状況に応じて2つのオプションが利用可能です。

ルーター全体の入力と出力の型の推論

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

単一プロシージャの型推論

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>;
}

tRPCクライアントへのアクセス

React Contextを使ったセットアップを採用している場合、useTRPCClientフックを使ってtRPCクライアントにアクセスできます。

tsx
import { useTRPCClient } from './trpc';
function Component() {
const trpcClient = useTRPCClient();
const result = await trpcClient.path.to.procedure.query({
/** input */
});
}
tsx
import { useTRPCClient } from './trpc';
function Component() {
const trpcClient = useTRPCClient();
const result = await trpcClient.path.to.procedure.query({
/** input */
});
}

React Contextを使わないセットアップの場合、代わりにグローバルクライアントインスタンスを直接インポートできます。

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