Middleware
Middleware lets you wrap setState() before the store applies the next value.
The public methods are:
pushMiddleware(middleware: Middleware<T>): void
unshiftMiddleware(middleware: Middleware<T>): voidThe internal middleware shape is equivalent to:
type SetStateAction<T> = T | ((prevState: T) => T);
type Middleware<T> = (
nextState: SetStateAction<T>,
next: (value: SetStateAction<T>) => void
) => void;Order
pushMiddleware() appends to the middleware array. unshiftMiddleware() prepends to the array. During setState(), the store uses reduceRight, so the first middleware in the array receives the update first.
const calls: string[] = [];
store.pushMiddleware((state, next) => {
calls.push("A before");
next(state);
calls.push("A after");
});
store.pushMiddleware((state, next) => {
calls.push("B before");
next(state);
calls.push("B after");
});
store.setState((prev) => prev);
// A before, B before, B after, A afterWith unshiftMiddleware(), the new middleware becomes the first one to receive future updates.
Transforming updates
Middleware can pass a different value or updater to next().
store.pushMiddleware((nextState, next) => {
next((prev) => {
const resolved = typeof nextState === "function" ? nextState(prev) : nextState;
return { ...resolved, updatedAt: Date.now() };
});
});Blocking updates
Middleware can block an update by not calling next(). Use this carefully and document the reason, because subscribers will not run and the caller receives no return value explaining the block.
store.pushMiddleware((nextState, next) => {
if (isInvalid(nextState)) return;
next(nextState);
});Side effects
Middleware runs synchronously inside setState(). Keep long work, network calls, and timers outside the middleware chain unless you intentionally want setState() to wait or throw.