UNPKG

koshin

Version:

Fine-grained reactive store for React using useSyncExternalStore

49 lines (48 loc) 1.53 kB
import { useSyncExternalStore } from "react"; class Koshin { data; changeListeners = new Set(); keySubscribers = {}; constructor(data) { this.data = data; } get(key) { return this.data[key]; } set(key, value) { const oldValue = this.data[key]; this.data[key] = value; this.changeListeners.forEach((cb) => cb(key, value, oldValue)); this.keySubscribers[key]?.forEach((cb) => cb(value)); } onChange(callback) { this.changeListeners.add(callback); return () => this.changeListeners.delete(callback); } subscribe(key, callback) { if (!this.keySubscribers[key]) { this.keySubscribers[key] = new Set(); } this.keySubscribers[key].add(callback); return () => this.keySubscribers[key].delete(callback); } } export function createStore(obj) { const store = new Koshin(obj); const proxy = {}; for (const key of Object.keys(obj)) { Object.defineProperty(proxy, key, { get: () => store.get(key), enumerable: true, }); } proxy.set = ((key, value) => store.set(key, value)); proxy.onChange = ((cb) => store.onChange(cb)); // ✅ signal hook for fine-grained reactive values proxy.signal = ((key) => { const value = useSyncExternalStore((cb) => store.subscribe(key, cb), () => store.get(key)); const setValue = (v) => store.set(key, v); return [value, setValue]; }); return proxy; }