mcpField
T
tanstack-query-v5

tanstack-query-v5

github.com/blockmatic開発1221 回インストール5 週間前に更新
リポジトリ

tanstack-query-v5 について

TanStack Query v5で非同期データを扱う、React・TypeScript開発者向けのスキルです。クエリキー、キャッシュ、ミューテーション、無限クエリを扱い、他の状態管理との使い分けも示します。

主な機能

  1. 01安定した階層型クエリキー
  2. 02非同期処理のキャッシュと再取得
  3. 03ミューテーションとクエリ無効化
  4. 04無限クエリとページネーション
  5. 05再利用可能なカスタムフック

活用シーン

  1. 01取得したサーバーデータのキャッシュ
  2. 02再利用可能なクエリロジックの作成
  3. 03ミューテーション後のデータ無効化
  4. 04無限スクロール型ページネーションの構築

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/blockmatic/basilic-skills --skill tanstack-query-v5

インストール後、Claude Code で/tanstack-query-v5を入力して読み込まれたか確認します。

詳細

スキル:tanstack-query

対象範囲

  • 対象:非同期処理、データ取得、キャッシュ、ミューテーション、無限クエリ、楽観的更新における TanStack Query v5+
  • 対象外:URL 状態管理(nuqs を使用)、グループ化された同期状態(ahooks.useSetState を使用)、localStorage の永続化(ahooks.useLocalStorageState を使用)

前提

  • TanStack Query v5+
  • フックをサポートする React 18+
  • 型推論のための TypeScript v5+
  • 共有クエリモジュールで手書きのクエリキータプルを使用(query-key-factory は任意)
  • QueryClientProvider をアプリのルートで設定

原則

  • キャッシュまたは状態管理のメリットがある非同期処理には、必ず TanStack Query を使用します
  • フックの近く、または共有 queries/ モジュールに配置した手書きのクエリキータプルを優先します
  • isLoading、error、isError の状態を手動で管理しないでください(フックから提供されます)
  • queryFn には、Promise を返す任意の関数を指定できます(HTTP 呼び出しに限りません)
  • データソースにかかわらず、すべての TanStack の機能は同じように動作します。キャッシュ、重複排除、バックグラウンドでの再取得、stale-while-revalidate などです

制約

必須
  • 安定した階層構造のクエリキーを使用します(手書きのタプル、または任意で query-key-factory)
  • 非同期処理には TanStack Query のフックを使用します(ローディング状態やエラー状態を手動で管理しないでください)
推奨
  • キャッシュのメリットがある非同期処理(HTTP、localStorage の読み取り、ローカル計算、ファイル操作)には TanStack Query を使用します
  • 再利用性を高めるため、クエリロジックをカスタムフックに切り出します
  • 複数のクエリを一貫性のあるフックにまとめます
  • 型安全なクエリには TypeScript のジェネリクスを使用します:useQuery<ResponseType>(...)
  • QueryClient のデフォルト設定(retry、staleTime)はプロバイダーレベルで設定します
  • プロジェクトですでに標準採用されている場合は、@lukemorales/query-key-factory を使用します
回避
  • 共有プレフィックスを使わずに、無関係なクエリキーを無効化処理でハードコードすること
  • ローディング状態やエラー状態を手動で管理すること(フックが提供する状態を使用してください)
  • URL で共有する状態に使用すること(代わりに nuqs を使用してください)
  • グループ化された同期状態に使用すること(代わりに ahooks.useSetState を使用してください)
  • キャッシュの必要がない単発の Promise に使用すること(イベントハンドラー内で通常の async/await を使用してください)

連携

  • URL 状態管理には nuqs-v2 と併用します(クエリは URL パラメーターに依存できます)
  • 同期状態には ahooks-v3 と併用します(フォーム状態には useSetState、永続化には useLocalStorageState を使用します)
  • OpenAPI で生成された API クライアントと連携します。React Query のフックは、それらのクライアントに対して手書きします
  • 状態管理の判断ツリーの一部です(React のルールを参照)

パターン

手書きのクエリキー

フックと同じ場所に配置する安定したクエリキー:

export const userKeys = {
  all: ['users'] as const,
  detail: (id: string) => [...userKeys.all, id] as const,
  list: (filters?: Filters) => [...userKeys.all, 'list', filters] as const,
}

// Usage
import { useQuery } from '@tanstack/react-query'
import { userKeys } from '@/queries/users'

const { data, isLoading, error } = useQuery({
  queryKey: userKeys.detail(userId),
  queryFn: () => fetchUser(userId),
})
任意の Query Key Factory

プロジェクトですでに @lukemorales/query-key-factory を使用している場合:

import { createQueryKeys } from '@lukemorales/query-key-factory'

export const users = createQueryKeys('users', {
  detail: (id: string) => ({
    queryKey: [id],
    queryFn: () => fetchUser(id),
  }),
})
汎用的な queryFn パターン

TanStack Query は、HTTP 呼び出しだけでなく、任意の非同期 queryFn を受け付けます:

// Server data fetching (traditional)
const { data } = useQuery({
  queryKey: ['users', userId],
  queryFn: () => fetchUser(userId),
})

// Local computation (no network)
const { data } = useQuery({
  queryKey: ['fibonacci', n],
  queryFn: () => computeFibonacci(n),
})

// localStorage read
const { data: settings } = useQuery({
  queryKey: ['user-settings'],
  queryFn: () => Promise.resolve(
    JSON.parse(localStorage.getItem('settings') || '{}')
  ),
})

// File operation
const { data } = useQuery({
  queryKey: ['file-content', path],
  queryFn: () => readFile(path),
})

使用する場合:自動キャッシュ、stale 状態の管理、または繰り返し実行のメリットがある非同期処理

使用しない場合:キャッシュの必要がない単発の Promise(イベントハンドラー内で通常の async/await を使用してください)

ミューテーションパターン

自動無効化を伴うミューテーション:

import { useMutation, useQueryClient } from '@tanstack/react-query'
import { userKeys } from '@/queries/users'

function useCreateUser() {
  const queryClient = useQueryClient()
  
  return useMutation({
    mutationFn: async (data: CreateUserInput) => {
      const response = await createUser(data)
      return response
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: userKeys.all })
    },
  })
}
無限クエリパターン

無限スクロールを使ったページネーション:

import { useInfiniteQuery } from '@tanstack/react-query'
import { users } from '@/queries/users'

const {
  data,
  fetchNextPage,
  hasNextPage,
  isFetchingNextPage,
} = useInfiniteQuery({
  queryKey: ['users', 'infinite'],
  queryFn: ({ pageParam = 0 }) => fetchUsers({ page: pageParam }),
  getNextPageParam: (lastPage, pages) => lastPage.nextPage,
  initialPageParam: 0,
})
QueryClient の設定

プロバイダーレベルでデフォルト設定を行います:

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { useState } from 'react'

function Providers({ children }: { children: ReactNode }) {
  const [queryClient] = useState(() => new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 60 * 1000, // 1 minute
        retry: 1,
        refetchOnWindowFocus: false,
      },
    },
  }))

  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  )
}
手書きのフックパターン

生成された OpenAPI クライアントをカスタムフックでラップします:

import { useQuery } from '@tanstack/react-query'
import { apiClient } from '@/lib/api-client'

export function useHealthCheck() {
  return useQuery({
    queryKey: ['health'],
    queryFn: () => apiClient.GET('/health').then(r => r.data),
  })
}
カスタムフックパターン

クエリロジックを再利用可能なフックに切り出します:

// hooks/useUser.ts
import { useQuery } from '@tanstack/react-query'
import { userKeys } from '@/queries/users'

export function useUser(userId: string) {
  return useQuery({
    queryKey: userKeys.detail(userId),
    queryFn: () => fetchUser(userId),
  })
}
状態管理の意思決定ツリー
  1. URL で共有可能な状態 → nuqs を使用します(フィルター、検索、タブ、ページネーション)
  2. URL に含めないグループ化された状態 → ahooks.useSetState を使用します(フォームの状態、ゲームエンジン、一時的な UI)
  3. 非同期処理 → TanStack Query を使用します(データ取得、ミューテーション、キャッシュ)
  4. localStorage への永続化 → ahooks.useLocalStorageState を使用します(環境設定、設定)
  5. 単純で独立した状態 → useState を使用します(まれなケースです。他の選択肢を優先してください)

TanStack Query を使用するケース:サーバーデータの取得、ミューテーション、楽観的更新、バックグラウンドでの再取得、キャッシュ、状態管理の利点があるあらゆる非同期処理

TanStack Query を使用しないケース:URL で共有可能な状態(nuqs を使用)、グループ化された同期状態(ahooks.useSetState を使用)、キャッシュが不要な単発の Promise

参考資料

コメント