TanStack Query
について
このスキルは、Reactアプリケーションでサーバー状態を管理するためのTanStack Query(React Query)実装に関する専門的なガイダンスを提供します。データ取得、キャッシング、ミューテーション、キャッシュ無効化などの主要機能を網羅しています。Reactプロジェクトにおけるクエリ、楽観的更新、ページネーション、無限スクロールに関する支援が必要な際にご利用ください。
クイックインストール
Claude Code
推奨/plugin add https://github.com/oriolrius/pki-manager-webgit clone https://github.com/oriolrius/pki-manager-web.git ~/.claude/skills/TanStack QueryこのコマンドをClaude Codeにコピー&ペーストしてスキルをインストールします
ドキュメント
TanStack Query (React Query)
Expert assistance with TanStack Query - Powerful data fetching for React.
Overview
TanStack Query manages server state in React:
- Caching: Automatic caching and background updates
- Refetching: Smart refetch strategies
- Mutations: Optimistic updates and cache invalidation
- DevTools: Built-in development tools
- TypeScript: Full TypeScript support
Installation
npm install @tanstack/react-query
npm install --save-dev @tanstack/react-query-devtools
Setup
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 1 minute
cacheTime: 5 * 60 * 1000, // 5 minutes
refetchOnWindowFocus: false,
},
},
});
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
useQuery
import { useQuery } from '@tanstack/react-query';
function CertificateList() {
const { data, isLoading, error } = useQuery({
queryKey: ['certificates'],
queryFn: () => fetch('/api/certificates').then(res => res.json()),
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.map(cert => (
<li key={cert.id}>{cert.subject}</li>
))}
</ul>
);
}
Query with Parameters
function CertificateDetail({ id }: { id: string }) {
const { data: certificate } = useQuery({
queryKey: ['certificate', id],
queryFn: () => fetch(`/api/certificates/${id}`).then(res => res.json()),
});
return <div>{certificate?.subject}</div>;
}
Dependent Queries
function Certificate({ id }: { id: string }) {
const { data: certificate } = useQuery({
queryKey: ['certificate', id],
queryFn: () => fetchCertificate(id),
});
// Only run if certificate exists
const { data: ca } = useQuery({
queryKey: ['ca', certificate?.caId],
queryFn: () => fetchCA(certificate.caId),
enabled: !!certificate?.caId,
});
return <div>{ca?.subject}</div>;
}
useMutation
import { useMutation, useQueryClient } from '@tanstack/react-query';
function CreateCertificate() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (newCert) => {
return fetch('/api/certificates', {
method: 'POST',
body: JSON.stringify(newCert),
});
},
onSuccess: () => {
// Invalidate and refetch
queryClient.invalidateQueries({ queryKey: ['certificates'] });
},
});
return (
<button
onClick={() => mutation.mutate({ subject: 'CN=example.com' })}
disabled={mutation.isPending}
>
{mutation.isPending ? 'Creating...' : 'Create Certificate'}
</button>
);
}
Optimistic Updates
const mutation = useMutation({
mutationFn: updateCertificate,
onMutate: async (newCert) => {
// Cancel outgoing refetches
await queryClient.cancelQueries({ queryKey: ['certificates'] });
// Snapshot previous value
const previousCerts = queryClient.getQueryData(['certificates']);
// Optimistically update
queryClient.setQueryData(['certificates'], (old) =>
old.map((cert) =>
cert.id === newCert.id ? newCert : cert
)
);
return { previousCerts };
},
onError: (err, newCert, context) => {
// Rollback on error
queryClient.setQueryData(['certificates'], context.previousCerts);
},
onSettled: () => {
// Refetch after success or error
queryClient.invalidateQueries({ queryKey: ['certificates'] });
},
});
Pagination
function CertificateList() {
const [page, setPage] = useState(1);
const { data, isLoading } = useQuery({
queryKey: ['certificates', page],
queryFn: () => fetchCertificates(page),
keepPreviousData: true, // Keep old data while fetching new
});
return (
<>
<ul>
{data?.certificates.map(cert => (
<li key={cert.id}>{cert.subject}</li>
))}
</ul>
<button onClick={() => setPage(p => p - 1)} disabled={page === 1}>
Previous
</button>
<button onClick={() => setPage(p => p + 1)} disabled={!data?.hasMore}>
Next
</button>
</>
);
}
Infinite Queries
import { useInfiniteQuery } from '@tanstack/react-query';
function InfiniteCertificates() {
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ['certificates'],
queryFn: ({ pageParam = 1 }) => fetchCertificates(pageParam),
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
initialPageParam: 1,
});
return (
<>
{data?.pages.map((page, i) => (
<div key={i}>
{page.certificates.map(cert => (
<div key={cert.id}>{cert.subject}</div>
))}
</div>
))}
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
>
{isFetchingNextPage ? 'Loading...' : 'Load More'}
</button>
</>
);
}
Cache Invalidation
const queryClient = useQueryClient();
// Invalidate all queries
queryClient.invalidateQueries();
// Invalidate specific query
queryClient.invalidateQueries({ queryKey: ['certificates'] });
// Invalidate query with params
queryClient.invalidateQueries({ queryKey: ['certificate', '123'] });
// Invalidate all queries starting with key
queryClient.invalidateQueries({ queryKey: ['certificates'], exact: false });
// Remove query from cache
queryClient.removeQueries({ queryKey: ['certificates'] });
// Reset query to initial state
queryClient.resetQueries({ queryKey: ['certificates'] });
Manual Cache Updates
// Set query data
queryClient.setQueryData(['certificate', '123'], newCertificate);
// Update query data
queryClient.setQueryData(['certificates'], (old) =>
old.map((cert) => cert.id === '123' ? updated : cert)
);
// Get query data
const certificates = queryClient.getQueryData(['certificates']);
// Prefetch query
await queryClient.prefetchQuery({
queryKey: ['certificate', '123'],
queryFn: () => fetchCertificate('123'),
});
Best Practices
- Query Keys: Use arrays for structured keys
['certificates', { status: 'active' }] - Stale Time: Set appropriate stale time for your data
- Cache Time: Keep data in cache longer than stale time
- Optimistic Updates: Improve UX with optimistic updates
- Error Handling: Handle errors gracefully
- Invalidation: Invalidate related queries on mutations
- Pagination: Use
keepPreviousDatafor better UX - DevTools: Use DevTools for debugging
- TypeScript: Define types for query data
- Prefetching: Prefetch data for better performance
Resources
- Documentation: https://tanstack.com/query
- GitHub: https://github.com/TanStack/query
GitHub リポジトリ
関連スキル
content-collections
メタThis skill provides a production-tested setup for Content Collections, a TypeScript-first tool that transforms Markdown/MDX files into type-safe data collections with Zod validation. Use it when building blogs, documentation sites, or content-heavy Vite + React applications to ensure type safety and automatic content validation. It covers everything from Vite plugin configuration and MDX compilation to deployment optimization and schema validation.
creating-opencode-plugins
メタThis skill provides the structure and API specifications for creating OpenCode plugins that hook into 25+ event types like commands, files, and LSP operations. It offers implementation patterns for JavaScript/TypeScript modules that intercept and extend the AI assistant's lifecycle. Use it when you need to build event-driven plugins for monitoring, custom handling, or extending OpenCode's capabilities.
polymarket
メタThis skill enables developers to build applications with the Polymarket prediction markets platform, including API integration for trading and market data. It also provides real-time data streaming via WebSocket to monitor live trades and market activity. Use it for implementing trading strategies or creating tools that process live market updates.
cloudflare-turnstile
メタThis skill provides comprehensive guidance for implementing Cloudflare Turnstile as a CAPTCHA-alternative bot protection system. It covers integration for forms, login pages, API endpoints, and frameworks like React/Next.js/Hono, while handling invisible challenges that maintain user experience. Use it when migrating from reCAPTCHA, debugging error codes, or implementing token validation and E2E tests.
