setState
setState() is the only method that changes the stored value.
setState(nextState: T | ((prevState: T) => T)): voidThe method accepts either a complete next value or an updater function. The updater receives the previous state and must return the complete next state.
Pass a complete value
const statusStore = new Store({ status: "idle", error: null as string | null });
statusStore.setState({ status: "loading", error: null });The value replaces the whole state. There is no shallow merge and no deep merge.
Pass an updater function
statusStore.setState((prev) => ({
...prev,
status: "success",
}));Use an updater when the next value depends on the previous value. This is the safest pattern for arrays, counters, maps, and object state that preserves fields.
Notification algorithm
The implementation resolves state in this order:
- Build a middleware chain from the registered middleware array.
- Run the chain with the
nextStatevalue or updater. - In the final step, call the updater if
nextStateis a function. - Compare the previous and resolved values with
Object.is. - If they are the same value or same reference, return without notifying.
- Otherwise store the resolved value and notify subscribers.
Replace, not merge
const store = new Store({ a: 1, b: 2 });
store.setState({ a: 2 });
console.log(store.getState()); // { a: 2 }If you expected { a: 2, b: 2 }, copy the previous object yourself.
store.setState((prev) => ({ ...prev, a: 2 }));Same reference means no notification
store.setState((prev) => prev);Returning prev is a no-op because Object.is(prev, prev) is true.
store.setState((prev) => {
prev.a = 3;
return prev;
});This mutation is worse than a no-op: the object was changed, but subscribers were not notified because the reference stayed the same. Always return a new object or array for object state.
Errors
setState() does not catch errors thrown by middleware, updater functions, or listeners. Let the caller decide whether to catch failures around the setState() call.