mutable-store
v2.0.0
Published
a mutable state management library for javascript
Maintainers
Readme
const documentation = `
Mutable Store
A lightweight, reactive store pattern for managing application state with simplicity and full control.
📦 Store
The Store function wraps your object and turns it into a reactive store where:
- All methods that start with
set_are considered mutative. - A global
subscribe(fn)method is provided to listen for changes. - It auto-subscribes to internal stores (i.e., nested stores created using
Store). - All function and internal store references are made read-only.
- Non-mutative methods (like
get_,action_) are untouched unless you mutate state directly inside them.
✅ Features
- ✅ Lightweight & framework-agnostic.
- ✅ Built-in subscription system.
- ✅ Auto-nested-store detection and tracking.
- ✅ Only mutative functions (
set_*) trigger updates. - ✅ Read-only enforcement for function and nested store props.
- ✅ Controlled and explicit design: mutations only when intended.
🔧 API
Store(mutableState: object)
Wrap your state and methods in a store.
Example:
import {Store} from "mutable-store";
const counter = Store({
count: 0,
set_increment() {
this.count++;
},
get_count() {
return this.count;
}
});
counter.subscribe(() => {
console.log("State changed!");
});
counter.set_increment(); // Triggers subscriber
console.log(counter.get_count()); // 1💡 Conventions
| Prefix | Purpose |
|-------------|----------------------------------------------|
| set_* | Used to mutate the store. Triggers updates. |
| get_* | Used to read state. Does not trigger updates. |
| action_* | Used to orchestrate multiple getters/setters. Can be async or sync. Does not directly mutate state unless via a set_*. |
⚠️ All mutative methods must start with
set_to trigger updates.
🔁 Nested Stores
You can pass nested stores as part of your store object: This lets you use existing store logic as a substore.
const loadingState = Store({
isLoading : false,
set_isLoading(isLoading) {
this.isLoading = isLoading;
}
});
const usersStore = Store({
users: [],
loadingState, // Nested store
action_fetchUsers() {
this.loadingState.set_isLoading(true);
fetch("/users").then(() => {
this.loadingState.set_isLoading(false);
});
}
});
const photosStore = Store({
photos: [],
loadingState, // Nested store
action_fetchPhotos() {
this.loadingState.set_isLoading(true);
fetch("/photos").then(() => {
this.loadingState.set_isLoading(false);
});
}
});
// Both stores use the same sub store
usersStore.action_fetchUsers(); // ✅ Triggers parent store subscription
photosStore.action_fetchPhotos(); // ✅ Triggers parent store subscriptioninheritance
You can create a store from a class that inherts props or methods from another class. This helps you to create a store from a class that extends another class.
class Counter {
count = 0;
set_increment() {
this.count++;
}
}
class Counter2 extends Counter {
city = "New York";
set_city(newCity:string) {
this.city = newCity;
this.set_increment();
}
}
const counter = Store(new Counter2());
console.log(counter); // {count: 0, city: "New York", set_increment: ƒ, set_city: ƒ}
counter.subscribe(() => {
console.log("Counter changed!");
});
counter.set_city("London"); // ✅ Triggers parent store subscription🧩 Internals
- All functions and nested stores are made non-configurable and non-writable.
subscribe(fn): Adds a listener to the store.___thisIsAMutableStore___: true: Internal flag to identify nested stores.___version___: 1: Reserved for future enhancements.
⚠️ Limitations & Warnings
subscribeis a reserved key — do not define it inside your store object.- Mutations must occur only inside
set_*methods. - You cannot add new properties to the store after creation (due to
Object.preventExtensions). - Deep reactivity is manual — this is intentional to keep it lightweight and predictable.
✅ Summary
This store is:
- Explicit.
- Predictable.
- Reactive when and only when you want it to be.
- Simple enough for small apps, yet composable for large ones.
