Mutations
Overview
Mutations in Zustand Q handle state updates such as adding, updating, or deleting data, with built-in support for asynchronous operations. They integrate seamlessly with Zustand's store, providing a straightforward way to manage side effects and state changes.
Configuration
Define mutations in the mutations object of createStore. Each mutation is configured with the following properties:
mutations: {
addItem: {
mutationFn: (variables: TVariables) => Promise<TData>,
onStore: (data: TData, set: SetState<TState>) => void,
onStart?: () => void,
onSuccess?: (data: TData) => void,
onError?: (error: unknown) => void,
onFinish?: (data?: TData, error?: unknown) => void,
},
},
mutationFn: An async function that performs the mutation, acceptingvariables(typeTVariables) and returning a promise ofTData.onStore: A function to update the store with the mutation result, using Zustand'ssetmethod.- Lifecycle Hooks: Optional callbacks (
onStart,onSuccess,onError,onFinish) for handling mutation lifecycle events.
Note:
TVariablesandTDataare generic types you define based on your mutation's input and output.
Usage
Call a mutation from your store with optional configuration overrides to customize its behavior:
const { mutate: addItem, isPending } = useMyStore().addItem({
onStart: () => console.log("Starting mutation..."),
onSuccess: (data) => console.log("Added:", data),
onError: (error) => console.error("Error:", error),
onFinish: (data, error) => console.log("Finished with:", data || error),
});
// Trigger the mutation with variables
addItem({ name: "New Item" });
Optional Configuration Overrides
When calling a mutation, you can provide an object with the following optional lifecycle hooks:
onStart?: () => void: Runs after theonStartfrom the configuration when the mutation begins.onSuccess?: (data: TData) => void: Runs after theonSuccessfrom the configuration on successful mutation.onError?: (error: unknown) => void: Runs after theonErrorfrom the configuration if an error occurs.onFinish?: (data?: TData, error?: unknown) => void: Runs after theonFinishfrom the configuration when the mutation completes, receivingdata(if successful) orerror(if failed).
Note: These lifecycle hooks execute after their counterparts in the mutation configuration, allowing you to extend or customize behavior without altering the original setup.
Return Values
Mutations return an object with the following properties:
mutate:(variables: TVariables) => Promise<TData>- Function to trigger the mutation with specified variables.isPending:boolean-truewhile the mutation is in progress.isSuccess:boolean-trueif the mutation completed successfully.isError:boolean-trueif the mutation failed.error:unknown | null- The error object if the mutation fails, ornullif no error.successAt:number | undefined- Timestamp (in milliseconds) of the last successful mutation, orundefinedif not yet successful.errorAt:number | undefined- Timestamp (in milliseconds) of the last error, orundefinedif no error occurred.status:"success" | "error" | "pending"- Current state of the mutation.
Example
Here’s a practical example of adding a cat to a list:
import { createStore } from "zustand-q";
import axios from "axios";
interface Cat {
id: string;
name: string;
}
export const useCatStore = createStore({
initialData: { cats: [] as Cat[] },
mutations: {
addCat: {
mutationFn: async (variables: { name: string }) =>
await axios.post<{ data: Cat }>(
"https://api.example.com/cats",
variables
),
onStore: (data, set) =>
set((state) => ({ cats: [...state.cats, data.data] })),
onStart: () => console.log("Mutation started from config"),
},
},
});
const App = () => {
const { cats, addCat } = useCatStore();
const {
mutate: createCat,
isPending,
status,
} = addCat({
onStart: () => console.log("Mutation started from override"),
onSuccess: (data) => console.log("Cat added:", data.data),
});
return (
<div>
<button onClick={() => createCat({ name: "Mimi" })} disabled={isPending}>
{isPending ? "Adding..." : "Add Cat"}
</button>
<p>Status: {status}</p>
<ul>
{cats.map((cat) => (
<li key={cat.id}>
{cat.name} (ID: {cat.id})
</li>
))}
</ul>
</div>
);
};
In this example:
- The
addCatmutation sends a POST request to add a new cat and updates thecatsarray in the store. - The
onStartfrom the configuration runs first, followed by theonStartfrom the override. - The component displays the mutation status and the updated cat list.