The current state
Indicates if this state is currently locked (true) or not (false)
Proxied access to state properties with automatic reconciliation.
Allows direct property access for both reading and writing state values.
Deep reactivity with the .live accessor
const state = new State({
nested: {
values: [1, 2, 3],
}
});
// Deep value assignment triggers state updates
state.live.nested.values[1] = 5;
// Array mutators are automatically reactive as well
state.live.nested.values.push(4);
state.live.nested.values.reverse();
Add a handler function for when this state changes.
Listeners are invoked in the order they are registered and are passed a reference to the internally managed state object as a readonly object.
Do not modify state values directly. Instead, use set or patch to make immutable updates.
Set a single stateful property while leaving other properties unchanged.
Triggers an update to all listeners if the value is different from before when compared using shallow equality.
Returns a deep clone of the current state data using structuredClone() to
make the copy.
The object returned from this function can be edited without modifying the actual internal state.
Perform a transaction-style set of actions defined within a function.
The provided function can do anything, beyond just setting state. Any uncaught errors thrown from within the function will cause the transaction to fail and the state to automatically roll back to the last valid state.
During a transaction, this state instance will be locked, preventing other changes.
Transactions will always result in a state update when successful.
Transactions with locking and rollback support
const state = new State({count: 1});
state.transaction(async (s) => {
// `s` is a full State object you can safely manipulate
s.set("count", 10);
});
state.get("count"); // => 10;
// Errors inside the transaction roll back the state
state.transaction(async (s) => {
s.set("count", 100);
throw new Error();
});
state.get("count");
Perform a transaction-style set of actions defined within an async function
The provided function can do anything, beyond just setting state. Any uncaught errors thrown from within the function will cause the transaction to fail and the state to automatically roll back to the last valid state.
During a transaction, this state instance will be locked, preventing other changes.
Transactions will always result in a state update when successful.
Transactions with locking and rollback support
const state = new State({count: 1});
// Awaiting the result of a transaction
await state.transactionAsync(async (s) => {
// `s` is a full State object you can safely manipulate
s.set("count", 10);
});
state.get("count"); // => 10;
// Errors inside the transaction roll back the state
await state.transactionAsync(async (s) => {
s.set("count", 100);
throw new Error();
});
state.get("count"); // => 10;
// If you forget to await the transaction, its still locked
state.transactionAsync(async (s) => {
await waitSeconds(1);
});
state.set("count", 1); // Error: State is locked!
Powerful and flexible state management with deep reactivity, transaction, and locking support.
The State class wraps stateful data and provides an API for mutating the data while listeners subscribe to meaningful updates.
Modify state with setters, mutators, transactions, or proxied updates with deep reactivity and array mutation support built in.
Example