obsidian/wiki/concepts/react-query-enabled-falsy-value.md
2026-05-01 09:38:54 +01:00

88 lines
3.6 KiB
Markdown

---
title: "React Query — enabled: !!value Silent Skip on Empty String"
aliases: [react-query-enabled, react-query-silent-skip, usequery-disabled]
tags: [react, react-query, frontend, debugging, gotcha, javascript]
sources:
- "daily/2026-04-30.md"
created: 2026-04-30
updated: 2026-04-30
---
# React Query — `enabled: !!value` Silent Skip on Empty String
When a React Query hook uses `enabled: !!someId` as a condition to fire the request, passing an empty string `""` as `someId` silently disables the query — `!!""` is `false`. The query never fires, the loading state resolves immediately with `undefined` data, and the UI shows nothing without any error or console warning.
## Key Points
- **`!!""` is `false`** — empty string is falsy in JavaScript; React Query treats it as "disabled"
- **Symptom:** component renders, no network request is made, data is `undefined` — looks like an API error but there is no request at all
- **Diagnosis:** in React Query DevTools or browser network tab — if there's no request for a query that should have fired, check `enabled` condition for falsy values
- **Fix option A:** use a separate hook or endpoint that doesn't require the ID (e.g., `GET /clients/all-projects` that returns all projects without a client filter)
- **Fix option B:** use `enabled: someId !== null && someId !== undefined` instead of `enabled: !!someId` to allow empty-string IDs
## Details
### The Failure Pattern
```typescript
// useProjects hook — requires clientId
const { data: projects } = useQuery(
["projects", clientId],
() => fetchProjects(clientId),
{ enabled: !!clientId } // ← silently disabled when clientId === ""
);
// In a form where the user hasn't selected a client yet:
const [clientId, setClientId] = useState(""); // empty string default
// → query never fires, projects is undefined, dropdown is empty
```
### Fix A: Separate "All Projects" Endpoint
When the UI needs ALL projects regardless of client selection, create a separate endpoint and hook that doesn't require a `clientId`:
```typescript
// New hook — no clientId required
export function useAllProjects() {
return useQuery(["projects", "all"], fetchAllProjects); // always enabled
}
// New backend endpoint
// GET /clients/all-projects → returns all projects user has access to
```
This avoids changing the `enabled` logic and makes the "fetch all" intent explicit.
### Fix B: Explicit Null Check
When the ID is legitimately optional but not always empty string:
```typescript
const { data: projects } = useQuery(
["projects", clientId],
() => fetchProjects(clientId),
{ enabled: clientId !== null && clientId !== undefined }
// now "" (empty string) is allowed and the query fires
);
```
### Common Falsy Values to Watch For in `enabled`
| Value | `!!value` | Often means |
|-------|-----------|-------------|
| `""` | `false` | Default state, unselected dropdown |
| `0` | `false` | First item in a zero-indexed list |
| `null` | `false` | Not yet loaded |
| `undefined` | `false` | Not yet loaded |
| `"0"` | `true` (string!) | String zero — may be surprising |
When in doubt, use `enabled: value !== null && value !== undefined` rather than `enabled: !!value`.
## Related Concepts
- [[wiki/concepts/zustand-async-hydration]] — another silent timing bug in React where state isn't ready when components mount
- [[wiki/tech-patterns/react-vite-typescript]] — React patterns in Oliver projects
## Sources
- [[daily/2026-04-30.md]] — Brief form: projects dropdown empty because `useProjects('')` was disabled by `enabled: !!clientId`; fixed with `useAllProjects()` hook + `GET /clients/all-projects` endpoint