Capítulo 20 de 80

Chapter 20: Disabling/Pausing Queries

Core Idea

enabled: false stops a query from auto-fetching (on mount, on invalidation, in the background) while keeping it declarative; skipToken is the TypeScript-safe equivalent for conditionally-fetchable queries, at the cost of breaking refetch().

Key Concepts

  • Effects of enabled: false: no fetch on mount; no background refetch; invalidateQueries/refetchQueries calls are ignored; starts in success state if cached data exists, otherwise pending/idle. Manual refetch() still works (except with skipToken).
  • Permanently disabling is an anti-pattern: it trades the declarative "run when dependencies say so" model for an imperative "fetch on click" one, and loses background refetching — if this is the pattern you keep reaching for, you probably want a lazy query instead.
  • Lazy queries: toggle enabled based on state that starts falsy and becomes truthy later (e.g. enabled: !!filter) — the query fires itself the moment the condition is met, no imperative trigger needed.
  • isLoading vs isPending: a lazy/disabled query is pending from the start even though nothing is fetching yet — isLoading (= isPending && isFetching) is the flag that's only true during an actual first fetch, and is the correct one to drive a spinner for lazy/disabled queries.
  • skipToken: pass it as the queryFn itself (not a boolean flag) to disable a query while preserving full type inference on the "enabled" branch — the trade-off is that refetch() throws a "Missing queryFn" error when the query is currently skipped, so use enabled: false instead if manual refetch() must keep working.

Code Examples

// Lazy query: fires itself once `filter` becomes truthy
const { data } = useQuery({
  queryKey: ['todos', filter],
  queryFn: () => fetchTodos(filter),
  enabled: !!filter,
})

// TypeScript-safe equivalent via skipToken
const { data } = useQuery({
  queryKey: ['todos', filter],
  queryFn: filter ? () => fetchTodos(filter) : skipToken,
})
  • What it demonstrates: the same lazy-fetch intent expressed via enabled vs. via skipToken — the latter keeps queryFn's type non-optional in the "enabled" branch.

Key Takeaways

  1. Reach for a lazy query (enabled toggled by state) instead of a permanently disabled one whenever the goal is "run once a condition is met," not "run only when I imperatively say so."
  2. Use isLoading (not isPending) to drive a spinner for lazy/conditional queries — isPending alone is true even before the query has ever fetched.
  3. Prefer skipToken for TypeScript projects that don't need refetch() on the disabled branch; fall back to enabled: false when manual refetch must keep working regardless of the enabled state.

Connects To

  • Queries: status/fetchStatus model that isLoading derives from.
  • TypeScript: broader inference discussion, including skipToken's role in it.