initial commit
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user