mutable-store
Version:
a mutable state management library for javascript
174 lines (126 loc) • 4.39 kB
Markdown
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";
```ts
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.
```ts
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 subscription
```
### inheritance
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.
```ts
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
- `subscribe` is 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.