@ripetchor/ssm
v1.0.3
Published
Simple State Manager
Readme
Simple State Manager
A lightweight, type-safe state management library for JavaScript applications. It provides a simple store for managing global state with features like batched updates, selectors, subscriptions, and configurable flushing strategies.
Features
- Type-Safe: Fully typed with generics for state and slices.
- Batched Updates: Queues state updates and flushes them in batches to optimize performance.
- Selectors: Extract specific slices of state without re-rendering the entire store.
- Subscriptions: Subscribe to state changes with optional equality checks and immediate callbacks.
- Flush Strategies: Control when updates are applied (
sync,previa microtask, orpostvia idle callback). - Immutable State: Uses
structuredClonefor deep copies to ensure immutability. - Destroy Method: Clean up subscribers and reset state when no longer needed.
- Helper Functions:
createStorefor instantiation andcreateSelectorfor reusable selectors.
Usage
Basic Example
Create a store, set state, and subscribe to changes.
import { createStore } from "@ripetchor/ssm";
interface AppState {
count: number;
user: string;
}
const initialState: AppState = { count: 0, user: "guest" };
const store = createStore(initialState);
// Get current state
console.log(store.getState()); // { count: 0, user: 'guest' }
// Set state
store.setState({ count: 1 });
// Subscribe to a slice
const unsubscribe = store.subscribe(
(state) => state.count,
(newCount) => console.log(`Count updated: ${newCount}`),
);
// Update state again
store.setState((prev) => ({ count: prev.count + 1 }));
// Unsubscribe
unsubscribe();
// Destroy the store
store.destroy();Advanced Usage
Selectors
Use selectors to derive computed values from the state.
import { createSelector } from "@ripetchor/ssm";
const countSelector = createSelector<AppState, number>((state) => state.count);
const selectedCount = store.select(countSelector);
console.log(selectedCount); // e.g., 2Subscription Options
Customize subscriptions with equality functions and immediate callbacks.
store.subscribe(
(state) => state.user,
(newUser) => console.log(`User: ${newUser}`),
{
immediate: false, // Don't call callback immediately
equalityFn: (a, b) => a === b, // Custom equality check
},
);Flush Options
Control when batched updates are flushed.
sync: Flush immediately (synchronous).pre: Flush in the next microtask (default, usingqueueMicrotask).post: Flush during idle time (usingrequestIdleCallbackwith 1s timeout).
store.setState({ user: "admin" }, { flush: "sync" });API Reference
createStore<TState>(initialState: TState): Store<TState>
Creates a new instance of the Store class.
Parameters:
initialState: The initial state object (genericTState).
Returns: A
Storeinstance.
Store<TState> Class
The core class for managing state.
Constructor
new Store(initialState: TState)
Initializes the store with a deep clone of the initial state.
Methods
getInitialState(): TStateReturns the original initial state (read-only).
getState(): TStateReturns the current state.
select<TSlice>(selector: SelectorFunction<TState, TSlice>): TSliceApplies a selector function to the current state and returns the selected slice.
- Parameters:
selector: A function that takes the state and returns a slice.
- Parameters:
setState(partial: ((prevState: TState) => Partial<TState>) | Partial<TState>, options?: SetStateOptions): voidQueues a state update. The update can be a partial object or a function that returns a partial based on the previous state.
- Parameters:
partial: Partial state or updater function.options: Optional object withflushstrategy ('sync' | 'pre' | 'post'). Defaults to{ flush: 'pre' }.
Updates are batched and applied during flush. Multiple updates in the queue are merged sequentially.
- Parameters:
subscribe<TSlice>(selector: SelectorFunction<TState, TSlice>, callback: (nextState: TSlice) => void, options?: SubscribeOptions<TSlice>): () => voidSubscribes to changes in a selected slice of state.
Parameters:
selector: Function to select the slice.callback: Function called with the new slice when it changes.options: Optional object:immediate: Boolean (defaulttrue): Call callback immediately with current slice.equalityFn: Function to compare previous and next slices (defaultObject.is).
Returns: An unsubscribe function.
Subscribers are notified only if the selected slice changes according to the equality function.
destroy(): voidClears all subscribers, empties the update queue, and resets the current state to the initial state.
createSelector<TState, TSlice>(selector: SelectorFunction<TState, TSlice>): (state: TState) => TSlice
Creates a reusable selector function.
Parameters:
selector: The selector logic.
Returns: A function that can be passed to
selectorsubscribe.
Types
The library exports several types from ./types:
SelectorFunction<TState, TSlice>:(state: TState) => TSliceSetStateOptions:{ flush: 'sync' | 'pre' | 'post' }SubscribeOptions<TSlice>:{ immediate?: boolean; equalityFn?: (a: TSlice, b: TSlice) => boolean }Subscriber<TState, TSlice>: Internal type for subscribers.UpdateFunction<TState>:(prev: TState) => Partial<TState>
Examples
Counter App
const store = createStore({ count: 0 });
const increment = () => store.setState((prev) => ({ count: prev.count + 1 }));
store.subscribe(
(state) => state.count,
(count) => console.log(`New count: ${count}`),
);
increment(); // Logs: New count: 1With Custom Equality
For arrays or objects, provide a deep equality function.
import { deepEqual } from "some-deep-equal-lib"; // Assume imported
store.subscribe(
(state) => state.items,
(newItems) => console.log("Items changed"),
{ equalityFn: deepEqual },
);Performance Considerations
- Batched updates reduce unnecessary re-renders.
- Use selectors to minimize subscription triggers.
structuredCloneis used for immutability, which may have performance overhead for very large states. Consider shallow copies if deep cloning isn't necessary.- Flush strategies allow tuning for responsiveness vs. performance.
Limitations
- No built-in middleware or effects (keep it simple).
- Assumes modern JS environments; polyfill
structuredClone,queueMicrotask, or requestIdleCallback` if needed for older browsers.
License
MIT
