Mutation Hook
Purpose
The Mutation Hook provides a specialized React hook designed to handle remote data mutations such as POST, PUT, DELETE, and PATCH requests in applications using the SWR data fetching strategy. It addresses the need for controlled, manual mutation triggers with robust state tracking, concurrency control, and integration with the SWR cache system. This hook enables developers to perform mutations that can optimistically update UI state, gracefully handle errors with rollback capabilities, and synchronize mutation results with the global cache.
Unlike the parent topic's broader focus on mutation mechanisms and types, this subtopic implements the concrete hook logic that developers use to trigger and manage mutations with fine-grained control over mutation lifecycle and state.
Functionality
At its core, the Mutation Hook exposes a mutation API that includes:
Manual Triggering: Developers call a
triggerfunction to initiate the mutation with optional arguments, controlling when and how the mutation occurs.State Tracking: The hook maintains reactive state properties for
data,error, andisMutating, enabling components to respond to mutation progress and results.Concurrency and Race Control: Each mutation is timestamped to discard out-of-order or stale mutation results, preventing race conditions and ensuring UI consistency.
Cache Integration: The mutation triggers SWR's
mutatefunction internally, enabling cache updates or invalidations based on mutation outcomes.Reset Capability: The hook provides a reset function to clear mutation state and cancel any pending mutation effects.
Configurable Behavior: Mutation behavior such as cache population, error throwing, and callbacks (
onSuccess,onError) can be customized via configuration options.
Key Workflow
Initialization: The hook stores references to the mutation key, fetcher function, and configuration options. It initializes internal state tracking mutation data, error, and loading status.
Trigger Mutation: When
triggeris called with an argument, it:Serializes the mutation key.
Validates presence of key and fetcher.
Merges default and user-provided options.
Records a timestamp to track the current mutation session.
Sets
isMutatingstate totrue.Calls SWR's
mutatewith the fetcher result to update cache and await the mutation outcome.Upon success, updates
dataand clears errors, invoking success callbacks.Upon failure, updates
error, clears loading, invokes error callbacks, and optionally throws.Ignores results from mutations that started before the latest timestamp to avoid race conditions.
Reset Mutation State: Calling reset clears all mutation state and updates the timestamp to discard ongoing mutations.
Illustrative Code Snippet
const trigger = useCallback(
async (arg: any, opts?: SWRMutationConfiguration<Data, Error>) => {
const [serializedKey, resolvedKey] = serialize(keyRef.current)
if (!fetcherRef.current || !serializedKey) {
throw new Error('Missing fetcher or key.')
}
const options = mergeObjects(
{ populateCache: false, throwOnError: true },
configRef.current,
opts
)
const mutationStartedAt = getTimestamp()
ditchMutationsUntilRef.current = mutationStartedAt
setState({ isMutating: true })
try {
const data = await mutate<Data>(
serializedKey,
fetcherRef.current(resolvedKey, { arg }),
{ ...options, throwOnError: true }
)
if (ditchMutationsUntilRef.current <= mutationStartedAt) {
setState({ data, isMutating: false, error: undefined })
options.onSuccess?.(data, serializedKey, options)
}
return data
} catch (error) {
if (ditchMutationsUntilRef.current <= mutationStartedAt) {
setState({ error: error as Error, isMutating: false })
options.onError?.(error as Error, serializedKey, options)
if (options.throwOnError) throw error
}
}
},
[]
)
Integration
The Mutation Hook integrates tightly with the core SWR library and complements other mutation-related subtopics such as state management and type definitions by providing the practical hook interface for remote mutations.
With Core SWR (
useSWR): It uses SWR's internal cache andmutatefunction to update or invalidate cached data in response to mutation results, ensuring consistency across components using the same key.With Mutation State Management: The hook leverages internal state utilities to track mutation status and notify React components of changes, supporting React concurrency features via
startTransition.With Middleware Architecture: The hook is wrapped with middleware composition using
withMiddleware, allowing extensibility and integration with other SWR features like optimistic UI updates or logging.With Other Mutation Subtopics: Types provided in the mutation types subtopic define the contract for the fetcher, configuration options, and trigger signatures used here, ensuring type safety and clear API boundaries.
By encapsulating mutation triggering and state tracking, this hook enables applications to perform remote mutations with reliable concurrency control and cache synchronization, seamlessly fitting into the larger SWR ecosystem.
Diagram
sequenceDiagram
participant Component
participant MutationHook
participant SWRCache
participant Fetcher
Component->>MutationHook: call trigger(arg)
MutationHook->>MutationHook: serialize key, validate fetcher/key
MutationHook->>MutationHook: set isMutating = true, record timestamp
MutationHook->>SWRCache: mutate(key, fetcher(resolvedKey, arg))
SWRCache->>Fetcher: execute fetcher function
Fetcher-->>SWRCache: return data or error
SWRCache-->>MutationHook: mutation result
alt success
MutationHook->>MutationHook: update data, isMutating=false, clear error
MutationHook->>Component: update state with data
else error
MutationHook->>MutationHook: update error, isMutating=false
MutationHook->>Component: update state with error
end
This sequence diagram illustrates the interaction flow when a mutation is triggered: the component initiates the mutation, the hook serializes and validates inputs, uses SWR's cache mutation mechanism to perform the fetcher call, and updates state based on success or failure. Timestamp checks ensure that only the latest mutation affects the state.