lovstudio:install-tanstack-query
install-tanstack-query — TanStack Query 初始化与重构
Use this skill to add TanStack Query to a project or refactor existing request state into a shared query/mutation layer.
When to Use
- The user asks to install or initialize TanStack Query / React Query.
- The project has repeated
useEffect + useState + fetch/invokerequest code. - The user wants all network-backed reads to share cache, refetch, invalidation, and loading/error behavior.
- A Tauri app uses many
invoke()reads that should be treated as server state. - The user mentions RTK Query but the project already uses, or prefers, TanStack Query.
Workflow
Step 1: Read Local Rules First
Before changing files, inspect local instructions and project shape:
pwd
find .. -name AGENTS.md -print
rg -n "@tanstack/react-query|react-query|@reduxjs/toolkit|createApi|useQuery|useMutation|fetch\\(|invoke\\(" package.json src app pages components 2>/dev/null
Honor project-specific constraints. If local instructions say not to run
build, do not run it. Prefer rg and inspect existing patterns before
adding new abstractions.
Step 2: Classify Request Code
Separate code into three buckets:
| Bucket | Examples | TanStack Query? |
|---|---|---|
| Server state reads | list/get/search/version/status/catalog/settings loaded from network or Tauri backend | Yes, useQuery |
| Server state writes | save/delete/toggle/install request, followed by cache changes | Yes, useMutation |
| Imperative side effects | terminal I/O, file open, clipboard, app relaunch, installer progress events, streaming channels | Usually no |
Do not force command-style effects into Query just to make the code look uniform. The goal is unified server state, not hiding every side effect.
Step 3: Install Only If Needed
Check package.json first. If TanStack Query is absent, detect package manager
from lockfiles and install:
pnpm add @tanstack/react-query
npm install @tanstack/react-query
yarn add @tanstack/react-query
bun add @tanstack/react-query
Only add persistence or devtools when the project already uses them or the user explicitly asks:
pnpm add @tanstack/react-query-persist-client @tanstack/query-sync-storage-persister
pnpm add -D @tanstack/react-query-devtools
Step 4: Add Provider
Add one top-level QueryClientProvider near the app root. Keep it consistent
with the existing architecture.
Recommended defaults:
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
refetchOnWindowFocus: false,
retry: false,
},
},
});
root.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>,
);
Adjust defaults for product needs. Data that changes frequently should use a
shorter staleTime, polling, streaming, or explicit invalidation.
Step 5: Create Shared Query Infrastructure
Prefer small local wrappers over scattering raw useQuery calls everywhere.
For Tauri apps:
import { invoke } from "@tauri-apps/api/core";
import {
useMutation,
useQuery,
useQueryClient,
type QueryKey,
type UseQueryOptions,
type UseMutationOptions,
} from "@tanstack/react-query";
type InvokeQueryOptions<TQueryFnData, TData> = Omit<
UseQueryOptions<TQueryFnData, Error, TData, QueryKey>,
"queryKey" | "queryFn"
>;
export function useInvokeQuery<TQueryFnData, TData = TQueryFnData>(
queryKey: QueryKey,
command: string,
args?: Record<string, unknown>,
options?: InvokeQueryOptions<TQueryFnData, TData>,
) {
return useQuery<TQueryFnData, Error, TData, QueryKey>({
queryKey,
queryFn: () => invoke<TQueryFnData>(command, args),
staleTime: Infinity,
refetchOnMount: false,
refetchOnWindowFocus: false,
...options,
});
}
type InvokeMutationOptions<T, V> = Omit<UseMutationOptions<T, Error, V>, "mutationFn">;
export function useInvokeMutation<T, V = void>(
command: string,
invalidateKeys?: QueryKey[],
options?: InvokeMutationOptions<T, V>,
) {
const queryClient = useQueryClient();
return useMutation<T, Error, V>({
mutationFn: (variables) => invoke<T>(command, variables as Record<string, unknown>),
...options,
onSuccess: (data, variables, context, mutation) => {
options?.onSuccess?.(data, variables, context, mutation);
invalidateKeys?.forEach((key) => {
queryClient.invalidateQueries({ queryKey: key });
});
},
});
}
Add stable query keys:
export const queryKeys = {
projects: ["projects"] as const,
settings: ["settings"] as const,
};
For REST apps, use the same structure but wrap the project's API client instead
of Tauri invoke().
Step 6: Refactor Incrementally
Start with duplicated and user-visible requests:
- Replace manual read state:
const { data = [], isLoading, error, refetch } = useInvokeQuery<Item[]>(
queryKeys.items,
"list_items",
);
- Replace write requests:
const saveItem = useInvokeMutation<Item, { item: Item }>(
"save_item",
[queryKeys.items],
);
-
Use
queryClient.setQueryData()for optimistic toggles when the UX needs instant feedback. -
Keep local UI state local. Dialog open state, form drafts, selected tabs, and filters usually do not belong in TanStack Query.
-
Do not break existing streaming subscriptions. If a stream already pushes data into
queryClient.setQueryData(), keep that pattern.
Step 7: Verification
Run the lightest reliable checks allowed by the repo:
pnpm exec tsc --noEmit --pretty false
npm run typecheck
yarn typecheck
bun run typecheck
Do not run heavy builds or dev server commands if local instructions forbid them. For UI-heavy changes, suggest browser verification or screenshots after type checks pass.
Final Response Checklist
Report:
- What query provider/wrappers/keys were added or reused.
- Which request flows moved to TanStack Query.
- Which imperative flows intentionally stayed command-driven.
- Which checks were run and whether any warnings remain.
More from lovstudio/dev-skills
lovstudio-skill-creator
>
10lovstudio-skill-optimizer
>
9lovstudio:cc-mv
Move a project folder AND migrate all its Claude Code state in one shot — session store, prompt-up-arrow history, running-session records. Use whenever the user wants to rename/move a project directory and keep `claude --resume` working. Handles sub-directory sessions automatically. 移动/重命名项目目录并迁移所有 CC 历史(session + prompt 历史 + 运行记录)。
8lovstudio-auto-context
>
8lovstudio-gh-access
>
8lovstudio-finder-action
>
8