Files
dotfiles/dot_agents/skills/tanstack-query-best-practices/rules/qk-serializable.md
T
Prad Nukala 6406208b35 shell: fish functions -> ~/.local/bin executables + bash/zsh ports; manage ~/.agents
- 16 cross-shell utilities converted to sh/bash chezmoi executables:
  ai c2p cp-appid extract_agents fab gdc gh-sonr-org-migrate ghg-clone
  ghn gho git-summary launch_omp_livediff_overlay lc-elir mdcp mdp pj
  (ghg-clone not named ghg: unrelated cargo binary owns that name)
- cwd/history helpers stay per-shell: yy c cd clip-last + ghg/ghn/gho
  cd-wrappers in bash/functions/ and zsh/functions/, loaded by rc files
- fish keeps only shell-state functions; converted ones now hit PATH
- cp-appid ignore gating moved to .local/bin path
- dot_agents/skills: 82 skills materialized from dotstate storage so
  dot_claude skill symlinks resolve on fresh bootstrap; dotstate
  profile/symlinks.json released .agents/skills .bashrc .zshrc
2026-08-10 15:15:06 -04:00

94 lines
2.5 KiB
Markdown

# qk-serializable: Ensure All Key Parts Are JSON-Serializable
## Priority: CRITICAL
## Explanation
Query keys are hashed using JSON serialization for cache lookups. Non-serializable values (functions, class instances, symbols, circular references) break caching and cause unexpected behavior. All parts of your query key must be JSON-serializable.
## Bad Example
```tsx
// Functions are not serializable
const { data } = useQuery({
queryKey: ['todos', () => 'active'], // Wrong: function in key
queryFn: fetchTodos,
})
// Class instances lose their prototype
class Filter {
constructor(public status: string) {}
isActive() { return this.status === 'active' }
}
const filter = new Filter('active')
const { data: todos } = useQuery({
queryKey: ['todos', filter], // Wrong: class instance
queryFn: () => fetchTodos(filter),
})
// Dates are technically serializable but become strings
const { data: events } = useQuery({
queryKey: ['events', new Date()], // Problematic: new Date() each render
queryFn: () => fetchEvents(date),
})
// Symbols are not serializable
const { data: settings } = useQuery({
queryKey: ['settings', Symbol('user')], // Wrong: symbol
queryFn: fetchSettings,
})
```
## Good Example
```tsx
// Use primitive values and plain objects
const { data } = useQuery({
queryKey: ['todos', 'active'],
queryFn: fetchTodos,
})
// Plain objects are fine
const filters = { status: 'active', priority: 'high' }
const { data: todos } = useQuery({
queryKey: ['todos', filters],
queryFn: () => fetchTodos(filters),
})
// For dates, use stable string representations
const dateKey = date.toISOString().split('T')[0] // '2024-01-15'
const { data: events } = useQuery({
queryKey: ['events', dateKey],
queryFn: () => fetchEvents(date),
})
// Arrays of primitives work correctly
const { data: users } = useQuery({
queryKey: ['users', { ids: [1, 2, 3] }],
queryFn: () => fetchUsers([1, 2, 3]),
})
```
## Serializable Types
**Safe to use:**
- Strings, numbers, booleans, null
- Plain objects (no prototype methods)
- Arrays of serializable values
- undefined (stripped but handled)
**Avoid:**
- Functions
- Class instances
- Symbols
- Date objects (use ISO strings instead)
- Map/Set (use arrays/objects instead)
- Circular references
## Context
- TanStack Query uses deterministic JSON hashing
- Object property order doesn't matter: `{ a: 1, b: 2 }` equals `{ b: 2, a: 1 }`
- Keys with `undefined` properties are normalized: `{ a: 1, b: undefined }` equals `{ a: 1 }`
- Test serialization: `JSON.stringify(queryKey)` should work without errors