notes

Log | Files | Refs | README

persistent_state.md (2227B)


      1 # Persistent State
      2 
      3 ```
      4                +-------------------+
      5 setState() --> |  PersistentStore  | --> subscribers notified
      6                +-------------------+
      7                       |    ^
      8                   save|    |load on init
      9                       v    |
     10                +-------------------+
     11                |   localStorage    |
     12                +-------------------+
     13 ```
     14 
     15 State that survives page reloads by syncing to localStorage (or sessionStorage).
     16 Includes a _version field so you can safely migrate stale data from older
     17 schemas.
     18 
     19 ```ts
     20 interface Versioned {
     21   _version: number;
     22 }
     23 
     24 class PersistentStore<T extends Versioned> {
     25   private state: T;
     26   private subscribers = new Set<(s: T) => void>();
     27   private key: string;
     28   private currentVersion: number;
     29   private migrate: (old: any) => T;
     30 
     31   constructor(key: string, initial: T, migrate: (old: any) => T) {
     32     this.key = key;
     33     this.currentVersion = initial._version;
     34     this.migrate = migrate;
     35     this.state = this.load(initial);
     36   }
     37 
     38   private load(initial: T): T {
     39     const raw = localStorage.getItem(this.key);
     40     if (!raw) return initial;
     41     const parsed = JSON.parse(raw);
     42     if (parsed._version !== this.currentVersion) return this.migrate(parsed);
     43     return parsed as T;
     44   }
     45 
     46   private save() {
     47     localStorage.setItem(this.key, JSON.stringify(this.state));
     48   }
     49 
     50   getState(): T {
     51     return { ...this.state };
     52   }
     53 
     54   setState(updates: Partial<T>) {
     55     this.state = { ...this.state, ...updates };
     56     this.save();
     57     this.subscribers.forEach((cb) => cb(this.getState()));
     58   }
     59 
     60   subscribe(cb: (s: T) => void) {
     61     this.subscribers.add(cb);
     62     return () => this.subscribers.delete(cb);
     63   }
     64 }
     65 
     66 // Usage
     67 interface ThemeState extends Versioned {
     68   theme: "light" | "dark";
     69   fontSize: number;
     70 }
     71 
     72 const themeStore = new PersistentStore<ThemeState>(
     73   "theme",
     74   { _version: 2, theme: "light", fontSize: 16 },
     75   (old) => ({ _version: 2, theme: old.theme ?? "light", fontSize: 16 }), // migrate v1 → v2
     76 );
     77 
     78 themeStore.setState({ theme: "dark" }); // persisted to localStorage immediately
     79 ```
     80 
     81 Use case: User preferences, session data, or any state that should survive a
     82 page refresh — theme, language, last-visited route, partially filled forms.