Skip to content

Instantly share code, notes, and snippets.

@thomaswilburn
Created December 11, 2023 17:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomaswilburn/1435276cf55a84c5f2535e5a427e4740 to your computer and use it in GitHub Desktop.
Save thomaswilburn/1435276cf55a84c5f2535e5a427e4740 to your computer and use it in GitHub Desktop.
ReactiveStore
var proxyRegistry = new WeakMap();
function observe(root, callback) {
var handler = {
get(target, property, rec) {
var value = target[property];
if (value instanceof Object && !(value instanceof Function)) {
if (proxyRegistry.has(value)) {
return proxyRegistry.get(value);
}
var proxy = new Proxy(value, handler);
proxyRegistry.set(value, proxy);
return proxy;
}
return value;
},
set(target, property, value) {
var previous = target[property];
if (previous == value) return true;
target[property] = value;
callback({ root, target, property, value, previous });
return true;
}
}
var proxy = new Proxy(root, handler);
return proxy;
}
export class ReactiveStore extends EventTarget {
#data = {};
#proxy = null;
#pending = false;
constructor(data = {}) {
super();
this.whenUpdated = this.whenUpdated.bind(this);
Object.assign(this.#data, data);
this.#proxy = observe(this.#data, this.whenUpdated);
}
get data() {
return this.#proxy;
}
raw() {
return this.#data;
}
whenUpdated(change) {
if (this.#pending) return;
this.#pending = true;
queueMicrotask(() => {
this.#pending = false;
this.dispatchEvent(new Event("updated"));
});
}
destroy() {
this.#data = null;
this.#proxy = null;
}
}
function tick() {
console.log("tick");
return new Promise(ok => requestAnimationFrame(ok));
}
var store = new ReactiveStore({ hello: "world" });
console.log(store.raw());
store.addEventListener("updated", () => console.log(store.raw()));
console.log("mutations");
store.data.goodbye = "world";
store.data.directory = {}
await tick();
console.log("no event");
store.data.goodbye = "world";
await tick();
console.log("add array");
store.data.directory.people = ["Thomas", "Belle", "Wallace"];
store.data.directory.people = [...store.data.directory.people, "Neko"];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment