Skip to content

Middleware

Log all state changes before updates.

import { createStore } from '@standard-store/store';
import { loggerMiddleware } from '@standard-store/store/middleware/logger';
const store = createStore(initialState, {
middleware: [loggerMiddleware],
});
store.setState({ count: 1 });
// Console output: { count: 0 } → { count: 1 }

Enforce value constraints per key before updates.

import { createStore } from '@standard-store/store';
import { validateMiddleware } from '@standard-store/store/middleware/validate';
const store = createStore(initialState, {
middleware: [
validateMiddleware({
theme: (v) => ["light", "dark"].includes(v),
count: (v) => typeof v === "number",
}),
],
});
store.setState({ theme: 'dark' }); // ✅ OK
store.setState({ theme: 'blue' }); // ❌ Throws error
interface StandardStoreMiddlewareV1 {
name?: string;
onGet?<T>(key: string, value: T, next: () => T): T;
onSet?<T>(key: string, value: T, next: () => void): void;
onRemove?(key: string, next: () => void): void;
}

Methods:

Method Description
onGet Intercept get operations (optional)
onSet Intercept set operations (optional)
onRemove Intercept remove operations (optional)
const customMiddleware = {
name: 'analytics',
async onSet(key, value, next) {
// Track state changes
console.log(`Setting ${key} to`, value);
const result = await next();
// Perform side effect after state is set
await analytics.track('state_change', {
key,
old: undefined,
new: value,
});
return result;
},
};

Combine multiple middleware in any order. They execute sequentially for hooks and in reverse order for after hooks.

import { loggerMiddleware } from '@standard-store/store/middleware/logger';
import { validateMiddleware } from '@standard-store/store/middleware/validate';
const store = createStore(initialState, {
middleware: [
loggerMiddleware, // Logs every operation
validateMiddleware(rules), // Validates before updates
customMiddleware, // Custom logic
],
});
const analyticsMiddleware = {
name: 'analytics',
async after(state, nextState) {
// Track state changes for analytics
await analytics.track('state_change', {
oldState: state,
newState: nextState,
});
},
};

Debounce state updates to reduce storage writes.

const debounceMiddleware = {
name: 'debounce-persist',
timeoutId: null as NodeJS.Timeout | null,
after(state, nextState) {
if (this.timeoutId) clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(() => {
localStorage.setItem('store', JSON.stringify(nextState));
}, 500); // Debounce by 500ms
},
};

Fetch data when user changes.

const sideEffectMiddleware = {
name: 'fetch-user',
async after(state, nextState) {
if (nextState.userId !== state.userId && nextState.userId) {
// User changed, fetch their profile data
const userData = await fetchUser(nextState.userId);
// Update store with fetched data
store.setState({
user: userData,
lastFetched: new Date()
});
}
},
};