MutationObserver
The MutationObserver class is used to observe mutations and subscribe to changes in mutation state. This is the core class that powers useMutation in framework integrations.
Import
import { MutationObserver } from '@tanstack/query-core'
Constructor
class MutationObserver<
TData = unknown,
TError = DefaultError,
TVariables = void,
TContext = unknown,
> {
constructor(
client: QueryClient,
options: MutationObserverOptions<TData, TError, TVariables, TContext>
)
}
Type Parameters
The type of data returned by the mutation function
TError
type
default:"DefaultError"
The type of error that can be thrown
The type of variables passed to the mutation function
The type of context returned by onMutate
Parameters
The QueryClient instance to use
options
MutationObserverOptions
required
Configuration options for the mutation observermutationFn
(variables: TVariables) => Promise<TData>
required
The function that performs the mutation
Optional unique key for the mutation
onMutate
(variables: TVariables) => Promise<TContext> | TContext
Callback fired before mutation function is executed
onSuccess
(data: TData, variables: TVariables, context: TContext) => Promise<unknown> | unknown
Callback fired when mutation succeeds
onError
(error: TError, variables: TVariables, context: TContext | undefined) => Promise<unknown> | unknown
Callback fired when mutation errors
onSettled
(data: TData | undefined, error: TError | null, variables: TVariables, context: TContext | undefined) => Promise<unknown> | unknown
Callback fired when mutation completes (success or error)
retry
boolean | number | (failureCount: number, error: TError) => boolean
default:"0"
Number of retry attempts or function to determine retries
retryDelay
number | (failureCount: number, error: TError) => number
Delay between retry attempts
Methods
mutate
mutate(
variables: TVariables,
options?: MutateOptions<TData, TError, TVariables, TContext>
): Promise<TData>
Execute the mutation with the given variables.
reset
Reset the mutation observer to its initial state.
setOptions
setOptions(
options: MutationObserverOptions<TData, TError, TVariables, TContext>
): void
Update the mutation observer options.
subscribe
subscribe(
listener: (result: MutationObserverResult<TData, TError, TVariables, TContext>) => void
): () => void
Subscribe to mutation state changes. Returns an unsubscribe function.
getCurrentResult
getCurrentResult(): MutationObserverResult<TData, TError, TVariables, TContext>
Get the current mutation result.
Result Type
interface MutationObserverResult<TData, TError, TVariables, TContext> {
context: TContext | undefined
data: TData | undefined
error: TError | null
failureCount: number
failureReason: TError | null
isError: boolean
isIdle: boolean
isPending: boolean
isPaused: boolean
isSuccess: boolean
mutate: (variables: TVariables, options?: MutateOptions) => void
mutateAsync: (variables: TVariables, options?: MutateOptions) => Promise<TData>
reset: () => void
status: 'idle' | 'pending' | 'error' | 'success'
submittedAt: number
variables: TVariables | undefined
}
Examples
Basic Usage
import { MutationObserver, QueryClient } from '@tanstack/query-core'
const queryClient = new QueryClient()
const observer = new MutationObserver(queryClient, {
mutationFn: async (newPost) => {
const response = await fetch('/api/posts', {
method: 'POST',
body: JSON.stringify(newPost),
})
return response.json()
},
})
// Subscribe to changes
const unsubscribe = observer.subscribe((result) => {
console.log('Mutation state:', result)
})
// Execute mutation
observer.mutate({ title: 'Hello', body: 'World' })
// Clean up
unsubscribe()
With Callbacks
import { MutationObserver, QueryClient } from '@tanstack/query-core'
const queryClient = new QueryClient()
const observer = new MutationObserver(queryClient, {
mutationFn: updatePost,
onMutate: async (variables) => {
// Cancel outgoing refetches
await queryClient.cancelQueries({ queryKey: ['posts'] })
// Snapshot previous value
const previous = queryClient.getQueryData(['posts'])
// Optimistically update
queryClient.setQueryData(['posts'], (old) => {
return old.map((post) =>
post.id === variables.id ? { ...post, ...variables } : post
)
})
return { previous }
},
onError: (err, variables, context) => {
// Rollback on error
queryClient.setQueryData(['posts'], context.previous)
},
onSettled: () => {
// Refetch after mutation
queryClient.invalidateQueries({ queryKey: ['posts'] })
},
})
Multiple Subscribers
import { MutationObserver } from '@tanstack/query-core'
const observer = new MutationObserver(queryClient, {
mutationFn: createPost,
})
// Multiple components can subscribe
const unsubscribe1 = observer.subscribe((result) => {
console.log('Subscriber 1:', result.status)
})
const unsubscribe2 = observer.subscribe((result) => {
console.log('Subscriber 2:', result.status)
})
// Both subscribers will be notified
observer.mutate(newPost)
// Clean up
unsubscribe1()
unsubscribe2()
Updating Options
import { MutationObserver } from '@tanstack/query-core'
const observer = new MutationObserver(queryClient, {
mutationFn: createPost,
})
// Update options later
observer.setOptions({
mutationFn: createPost,
onSuccess: (data) => {
console.log('Post created:', data)
},
})
Manual State Management
import { MutationObserver } from '@tanstack/query-core'
const observer = new MutationObserver(queryClient, {
mutationFn: updateUser,
})
// Get current state
const result = observer.getCurrentResult()
console.log('Status:', result.status)
console.log('Data:', result.data)
console.log('Error:', result.error)
// Reset to initial state
observer.reset()
With Retry Logic
import { MutationObserver } from '@tanstack/query-core'
const observer = new MutationObserver(queryClient, {
mutationFn: createPost,
retry: 3,
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
})
Notes
MutationObserver is the low-level class that framework integrations build upon
- In React,
useMutation creates and manages a MutationObserver instance
- Observers can have multiple subscribers
- The observer manages a single mutation instance at a time
- Calling
mutate() multiple times creates new mutation instances
- Each mutation instance has its own state and lifecycle
- The observer automatically handles retry logic based on configuration
- Subscribers are notified synchronously when state changes