feat(frontend): add createStore factory + central localStorage registry
- lib/store.ts: createStore<T> — one observable-value primitive (get/set/subscribe/use) replacing the hand-rolled listeners/subscribe/emit triad several modules each had. - lib/storage.ts: LS key registry (every siftlode.* key in one place), readMerged/ readJSON/writeJSON (the try/JSON.parse/merge/catch done once), and usePersistedState (the reactive persisted-string hook, generalizing usePersistedTab).
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import { useSyncExternalStore } from "react";
|
||||
|
||||
// A minimal observable value shared across React components AND non-React code (the api
|
||||
// layer, websocket handlers, etc.). Replaces the hand-rolled `listeners`/`subscribe`/`emit`
|
||||
// triad that several modules each re-implemented slightly differently.
|
||||
export interface Store<T> {
|
||||
get(): T;
|
||||
set(next: T | ((prev: T) => T)): void;
|
||||
subscribe(listener: () => void): () => void;
|
||||
/** Subscribe from a component; re-renders on every change. */
|
||||
use(): T;
|
||||
}
|
||||
|
||||
export function createStore<T>(initial: T): Store<T> {
|
||||
let value = initial;
|
||||
const listeners = new Set<() => void>();
|
||||
const get = () => value;
|
||||
const subscribe = (l: () => void) => {
|
||||
listeners.add(l);
|
||||
return () => {
|
||||
listeners.delete(l);
|
||||
};
|
||||
};
|
||||
return {
|
||||
get,
|
||||
set(next) {
|
||||
value = typeof next === "function" ? (next as (p: T) => T)(value) : next;
|
||||
listeners.forEach((l) => l());
|
||||
},
|
||||
subscribe,
|
||||
use: () => useSyncExternalStore(subscribe, get, get),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user