170 lines
4.1 KiB
Markdown
170 lines
4.1 KiB
Markdown
# mut-mutation-state: Use useMutationState for Cross-Component Mutation Tracking
|
|
|
|
## Priority: MEDIUM
|
|
|
|
## Explanation
|
|
|
|
`useMutationState` allows you to access mutation state from anywhere in your component tree, not just where `useMutation` was called. Use it to show loading indicators, display optimistic updates, or track pending mutations across components.
|
|
|
|
## Bad Example
|
|
|
|
```tsx
|
|
// Prop drilling mutation state
|
|
function App() {
|
|
const mutation = useMutation({ mutationFn: createPost })
|
|
|
|
return (
|
|
<div>
|
|
<Header isPending={mutation.isPending} />
|
|
<Sidebar isPending={mutation.isPending} />
|
|
<Content mutation={mutation} />
|
|
<Footer isPending={mutation.isPending} />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// Or using context for every mutation
|
|
const MutationContext = createContext<UseMutationResult | null>(null)
|
|
```
|
|
|
|
## Good Example
|
|
|
|
```tsx
|
|
// Define mutation with a key
|
|
const useCreatePost = () => useMutation({
|
|
mutationKey: ['create-post'],
|
|
mutationFn: createPost,
|
|
})
|
|
|
|
// In the component that triggers mutation
|
|
function CreatePostButton() {
|
|
const mutation = useCreatePost()
|
|
|
|
return (
|
|
<button onClick={() => mutation.mutate(newPost)}>
|
|
Create Post
|
|
</button>
|
|
)
|
|
}
|
|
|
|
// In any other component - track mutation state
|
|
function GlobalLoadingIndicator() {
|
|
const pendingMutations = useMutationState({
|
|
filters: { status: 'pending' },
|
|
select: (mutation) => mutation.state.variables,
|
|
})
|
|
|
|
if (pendingMutations.length === 0) return null
|
|
|
|
return (
|
|
<div className="global-loading">
|
|
Saving {pendingMutations.length} item(s)...
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Good Example: Optimistic UI in Separate Component
|
|
|
|
```tsx
|
|
// Mutation defined in form
|
|
function TodoForm() {
|
|
const createTodo = useMutation({
|
|
mutationKey: ['create-todo'],
|
|
mutationFn: (todo: NewTodo) => api.createTodo(todo),
|
|
})
|
|
|
|
return <form onSubmit={...}>...</form>
|
|
}
|
|
|
|
// Optimistic display in list (different component)
|
|
function TodoList() {
|
|
const { data: todos } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
|
|
|
|
// Get pending todo creations
|
|
const pendingTodos = useMutationState({
|
|
filters: {
|
|
mutationKey: ['create-todo'],
|
|
status: 'pending',
|
|
},
|
|
select: (mutation) => mutation.state.variables as NewTodo,
|
|
})
|
|
|
|
return (
|
|
<ul>
|
|
{/* Existing todos */}
|
|
{todos?.map(todo => (
|
|
<TodoItem key={todo.id} todo={todo} />
|
|
))}
|
|
|
|
{/* Optimistic todos (pending creation) */}
|
|
{pendingTodos.map((todo, index) => (
|
|
<TodoItem
|
|
key={`pending-${index}`}
|
|
todo={{ ...todo, id: `temp-${index}` }}
|
|
isPending
|
|
/>
|
|
))}
|
|
</ul>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Good Example: Track Specific Mutations
|
|
|
|
```tsx
|
|
function PostActions({ postId }: { postId: string }) {
|
|
// Track if THIS post is being deleted
|
|
const isDeletingThisPost = useMutationState({
|
|
filters: {
|
|
mutationKey: ['delete-post', postId],
|
|
status: 'pending',
|
|
},
|
|
select: () => true,
|
|
}).length > 0
|
|
|
|
// Track if THIS post is being updated
|
|
const isUpdatingThisPost = useMutationState({
|
|
filters: {
|
|
mutationKey: ['update-post', postId],
|
|
status: 'pending',
|
|
},
|
|
select: () => true,
|
|
}).length > 0
|
|
|
|
return (
|
|
<div>
|
|
<button disabled={isDeletingThisPost || isUpdatingThisPost}>
|
|
{isDeletingThisPost ? 'Deleting...' : 'Delete'}
|
|
</button>
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Filters Reference
|
|
|
|
```tsx
|
|
useMutationState({
|
|
filters: {
|
|
mutationKey: ['key'], // Match mutation key
|
|
status: 'pending', // 'idle' | 'pending' | 'success' | 'error'
|
|
predicate: (mutation) => bool, // Custom filter function
|
|
},
|
|
select: (mutation) => {
|
|
// Transform each matching mutation
|
|
// mutation.state contains: variables, data, error, status, etc.
|
|
return mutation.state.variables
|
|
},
|
|
})
|
|
```
|
|
|
|
## Context
|
|
|
|
- Requires `mutationKey` on mutations you want to track
|
|
- Returns array of selected values from matching mutations
|
|
- Updates reactively as mutations progress
|
|
- Use `status` filter to track pending/success/error states
|
|
- Enables optimistic UI without prop drilling
|
|
- Pairs with `mutationKey` arrays for granular tracking (e.g., `['delete-post', postId]`)
|