Files
simstudioai--sim/.cursor/commands/react-query-best-practices.md
wehub-resource-sync d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

2.8 KiB

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:

  1. https://tkdodo.eu/blog/practical-react-query — foundational defaults, custom hooks, avoiding local state copies
  2. https://tkdodo.eu/blog/effective-react-query-keys — key factory pattern, hierarchical keys, fuzzy invalidation
  3. https://tkdodo.eu/blog/react-query-as-a-state-manager — React Query IS your server state manager

Rules to enforce

Query key factories

  • Every file in hooks/queries/ must have a hierarchical key factory with an all root key
  • Keys must include intermediate plural keys (lists, details) for prefix invalidation
  • Key factories are colocated with their query hooks, not in a global keys file

Query hooks

  • Every queryFn must forward signal for request cancellation
  • Every query must have an explicit staleTime (default 0 is almost never correct), assigned from a named exported constant — never an inline numeric literal. A server-side prefetch hydrating the same query key must import and reuse that constant instead of restating the number
  • keepPreviousData / placeholderData only on variable-key queries (where params change), never on static keys
  • Use enabled to prevent queries from running without required params

Mutations

  • Use onSettled (not onSuccess) for cache reconciliation — it fires on both success and error
  • For optimistic updates: save previous data in onMutate, roll back in onError
  • Use targeted invalidation (entityKeys.lists()) not broad (entityKeys.all) when possible
  • Don't include mutation objects in useCallback deps — .mutate() is stable

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 — setQueryData is for optimistic updates only
  • Forms are the one deliberate exception: copy server data into local form state with staleTime: Infinity

Steps

  1. Read the references above to understand the guidelines
  2. Analyze the specified scope against the rules listed above
  3. If fix=true, apply the fixes. If fix=false, propose the fixes without applying.