notes

Log | Files | Refs | README

path_based_state.md (2279B)


      1 # Path-Based State (Deep State)
      2 
      3 ```
      4 State Tree
      5 +----------------------------------+
      6 |  app                             |
      7 |    user                          |
      8 |      profile                     |
      9 |        name: "Alice"  <--- path  |
     10 |        age:  30                  |
     11 |    cart                          |
     12 |      count: 2                    |
     13 +----------------------------------+
     14 
     15 subscribe("user.profile.name") --> only fires when name changes
     16 subscribe("cart.count")        --> only fires when count changes
     17 ```
     18 
     19 Instead of subscribing to the whole state object, subscribers listen to a
     20 specific path within a nested state tree — e.g. "user.profile.name". Only
     21 changes to that exact path trigger their callback.
     22 
     23 ```ts
     24 type Path<T> = string; // e.g. "user.profile.name"
     25 
     26 class DeepStore<T extends object> {
     27   private state: T;
     28   private subscribers = new Map<string, Set<(val: unknown) => void>>();
     29 
     30   constructor(initial: T) {
     31     this.state = initial;
     32   }
     33 
     34   subscribe<V>(path: Path<T>, cb: (val: V) => void): () => void {
     35     if (!this.subscribers.has(path)) this.subscribers.set(path, new Set());
     36     this.subscribers.get(path)!.add(cb as (val: unknown) => void);
     37     return () =>
     38       this.subscribers.get(path)?.delete(cb as (val: unknown) => void);
     39   }
     40 
     41   set(path: Path<T>, value: unknown) {
     42     const keys = path.split(".");
     43     let cursor: any = this.state;
     44     for (let i = 0; i < keys.length - 1; i++) cursor = cursor[keys[i]];
     45     cursor[keys[keys.length - 1]] = value;
     46     this.subscribers.get(path)?.forEach((cb) => cb(value));
     47   }
     48 
     49   get(path: Path<T>): unknown {
     50     return path.split(".").reduce((obj: any, key) => obj?.[key], this.state);
     51   }
     52 }
     53 
     54 // Usage
     55 interface AppState {
     56   user: { profile: { name: string; age: number } };
     57   cart: { count: number };
     58 }
     59 
     60 const store = new DeepStore<AppState>({
     61   user: { profile: { name: "Alice", age: 30 } },
     62   cart: { count: 2 },
     63 });
     64 
     65 store.subscribe<string>(
     66   "user.profile.name",
     67   (name) => console.log(`Name changed: ${name}`),
     68 );
     69 store.set("user.profile.name", "Bob"); // fires callback
     70 store.set("cart.count", 5); // does NOT fire name callback
     71 ```
     72 
     73 Use case: Large nested state trees where you want granular, performant
     74 subscriptions — only the parts of the UI that care about a specific slice
     75 re-render.