initial commit

This commit is contained in:
Jose Selesan
2026-05-28 14:33:16 -03:00
commit 7bc3d9f898
211 changed files with 161253 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
# mut-invalidate-queries: Always Invalidate Related Queries After Mutations
## Priority: HIGH
## Explanation
After mutations, invalidate all queries whose data might be affected. This ensures the cache stays synchronized with the server. Forgetting to invalidate related queries leads to stale UI data.
## Bad Example
```tsx
// No invalidation - cache remains stale
const createTodo = useMutation({
mutationFn: (newTodo) => api.createTodo(newTodo),
// Missing onSuccess handler - todo list won't show new item
})
// Partial invalidation - misses related queries
const deleteTodo = useMutation({
mutationFn: (todoId) => api.deleteTodo(todoId),
onSuccess: () => {
// Only invalidates list, not summary/counts
queryClient.invalidateQueries({ queryKey: ['todos', 'list'] })
// Missing: ['todos', 'count'], ['todos', 'completed-count'], etc.
},
})
```
## Good Example
```tsx
// Comprehensive invalidation
const createTodo = useMutation({
mutationFn: (newTodo) => api.createTodo(newTodo),
onSuccess: () => {
// Invalidate all todo-related queries
queryClient.invalidateQueries({ queryKey: ['todos'] })
},
})
// Targeted invalidation with all affected queries
const updateTodo = useMutation({
mutationFn: ({ id, data }) => api.updateTodo(id, data),
onSuccess: (data, { id }) => {
// Specific todo
queryClient.invalidateQueries({ queryKey: ['todos', id] })
// Lists that might contain this todo
queryClient.invalidateQueries({ queryKey: ['todos', 'list'] })
// If todo status changed, invalidate filtered views
queryClient.invalidateQueries({ queryKey: ['todos', 'completed'] })
queryClient.invalidateQueries({ queryKey: ['todos', 'active'] })
},
})
// Cross-entity invalidation
const assignTodoToUser = useMutation({
mutationFn: ({ todoId, userId }) => api.assignTodo(todoId, userId),
onSuccess: (data, { todoId, userId }) => {
// Invalidate the todo
queryClient.invalidateQueries({ queryKey: ['todos', todoId] })
// Invalidate user's assigned todos
queryClient.invalidateQueries({ queryKey: ['users', userId, 'todos'] })
// Invalidate previous assignee's list if available
if (data.previousAssignee) {
queryClient.invalidateQueries({
queryKey: ['users', data.previousAssignee, 'todos'],
})
}
},
})
```
## Pattern: Mutation with Variables Access
```tsx
const mutation = useMutation({
mutationFn: updatePost,
onSuccess: (
data, // Server response
variables, // What you passed to mutate()
context // What onMutate returned
) => {
// Use variables to know which queries to invalidate
queryClient.invalidateQueries({ queryKey: ['posts', variables.id] })
queryClient.invalidateQueries({ queryKey: ['posts', 'list', variables.category] })
},
})
```
## Pattern: Invalidate or Update Directly
```tsx
// Option 1: Invalidate and refetch
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] })
}
// Option 2: Update cache directly (no network request)
onSuccess: (newTodo) => {
queryClient.setQueryData(['todos'], (old: Todo[]) => [...old, newTodo])
}
// Option 3: Hybrid - update one, invalidate others
onSuccess: (newTodo) => {
// Immediately add to list
queryClient.setQueryData(['todos', 'list'], (old: Todo[]) => [...old, newTodo])
// Invalidate counts/summaries for eventual consistency
queryClient.invalidateQueries({ queryKey: ['todos', 'count'] })
}
```
## Context
- Place invalidation in `onSuccess` for successful mutations
- Use `onSettled` if you want to invalidate regardless of success/failure
- Think about all UI surfaces that display related data
- For complex relationships, consider a centralized invalidation helper
- Using hierarchical query keys makes this easier (see `qk-hierarchical-organization`)