react-query-best-practices
GitHub审计 React Query 使用规范,确保键工厂、缓存策略及服务端状态管理的最佳实践,支持修复或仅提案更改。
Trigger Scenarios
Install
npx skills add simstudioai/sim --skill react-query-best-practices -g -y
SKILL.md
Frontmatter
{
"name": "react-query-best-practices",
"description": "Audit React Query usage for best practices — key factories, staleTime, mutations, and server state ownership",
"argument-hint": "[scope] [fix=true|false]"
}
React Query Best Practices
Arguments:
- scope: what to analyze (default: your current changes). Examples: "diff to main", "PR #123", "src/hooks/queries/", "whole codebase"
- fix: whether to apply fixes (default: true). Set to false to only propose changes.
User arguments: $ARGUMENTS
Context
This codebase uses React Query (TanStack Query) as the single source of truth for all server state. All query hooks live in hooks/queries/. Zustand is used only for client-only UI state. Server data must never be duplicated into useState or Zustand outside of mutation callbacks that coordinate cross-store state.
References
Read these before analyzing:
- https://tkdodo.eu/blog/practical-react-query — foundational defaults, custom hooks, avoiding local state copies
- https://tkdodo.eu/blog/effective-react-query-keys — key factory pattern, hierarchical keys, fuzzy invalidation
- https://tkdodo.eu/blog/react-query-as-a-state-manager — React Query IS your server state manager
Rules to enforce
Query keys and hooks
Enforce CLAUDE.md "React Query" and .claude/rules/sim-queries.md (key factory with all + plural prefixes, signal forwarding, named staleTime constants reused by prefetches, keepPreviousData only on variable keys, requestJson boundary). Additionally:
- Key factories live next to their hooks — except a factory, standalone fetcher/mapper, or
staleTimeconstant that a server module (aprefetch.ts, route, block, trigger) imports, which must live in a non-'use client'module underhooks/queries/utils/per.claude/rules/sim-queries.md(a'use client'export called from the server crashes SSR) - Use
enabledto prevent queries from running without required params - Warm data for hover/focus intent with
queryClient.prefetchQueryand sharedqueryOptions; never temporarily enable a mounted hidden observer, which can remain active after focus restoration and refetch data for closed UI - When gating a query by view or modal state, move every consumer to the active query too: imperative refresh/pagination, loading and error feedback, and data-derived controls must never read a disabled query or placeholder data from a previous key
- Compose caller-controlled
enabledoptions with required-param guards (Boolean(id) && (options?.enabled ?? true)). Never spread options after an internal guard, because{ enabled: true }can silently re-enable an invalid request. - A disabled query can still report
isPending: true. Aggregate loading state only for queries that are applicable/enabled, or an optional query can hold the whole surface in a permanent loading state. - Deferred authorization or policy queries must fail closed. Do not give pending/error data the same fallback as a successfully loaded unrestricted policy; disable guarded actions until the policy query succeeds.
- Server prefetches must call the authorized use case, apply the route presenter/response schema, and reuse the client's exact key, mapper, and stale time. Keep all fallible auth/read/parse work inside
queryFnso an optional warm cannot fail the page, and never bypass a route that redacts fields.
Mutations
Enforce CLAUDE.md "Mutation Hooks" (targeted invalidation, onMutate/onError rollback, mutation objects out of useCallback deps). Additionally:
- Plain mutations invalidate in
onSuccess; optimistic mutations reconcile inonSettled(fires on success and error) with rollback inonError— see.claude/rules/sim-queries.md"Mutation Hook" / "Optimistic Updates"
Server state ownership
- Never copy query data into useState. Use query data directly in components.
- Never copy query data into Zustand stores (exception: mutation callbacks that coordinate cross-store state like temp ID replacement)
- The query cache is not a local state manager —
setQueryDatais for optimistic updates and the server-prefetch seeding case in.claude/rules/sim-queries.md"Server prefetching", nothing else - Forms are the one deliberate exception (a keyed form child initialized lazily from loaded query data) — the pattern is owned by
/you-might-not-need-an-effect"Query-backed forms"; do not duplicate its finding
Steps
- Read the references above to understand the guidelines
- Analyze the specified scope against the rules listed above
- If fix=true, apply the fixes. If fix=false, propose the fixes without applying.
Version History
-
6dfb467
Current 2026-09-23 06:36
移除过时模式与冗余内容,修正提供者矩阵及仓库特定声明
-
336ec07
2026-08-29 03:06
新增 query hooks 规则:处理 enabled 选项组合、禁用查询的加载状态聚合、权限查询失败关闭及服务端预取逻辑。
- ceda457 2026-08-20 15:29


