Skip to content

Instantly share code, notes, and snippets.

@KargJonas
Last active July 24, 2019 19:31
Show Gist options
  • Select an option

  • Save KargJonas/a0aac5b1ad189676da58cb11a032cf9a to your computer and use it in GitHub Desktop.

Select an option

Save KargJonas/a0aac5b1ad189676da58cb11a032cf9a to your computer and use it in GitHub Desktop.
// Selecting an element
const counterElement = document.querySelector("#counter");
// Creating a mutable (the "new" is unnecessary, just looks cool)
const state = new Mutable({
count: 0
});
// Subscribing to it (update the content of our element, when mutable changes)
state.$subscribe(() => {
counterElement.innerHTML = state.count;
});
// Incrementing the counter => automatically updates element's innerHTML
setInterval(
() => state.count++,
1000
);
/**
* Mutable:
* A proxy with some more features.
* - Multi-Subscriptions on the fly
* - Optional Recursion (sub objects will also be proxies)
*
* Mutable(<initialObject>, <recursive>) // create a Mutable, parameters default to ({}, false)
* <>.$subscribe(<subscriberFunc>) // subscribe to the Mutable - returns an id
* <>.$unsubscribe(<id>) // unsubscribes a function by it's id
* <>.$callSubscriptions() // call all subscriber-functions
*
* I built it this weird because Proxy has no prototype and
* thus is not extensible. The setter checks, if the property
* begins with a "$" and if so, "switches the scope" to the
* one of the Proxy's handler. This allows us to access methods
* and data inside the handler within those methods.
*
* Subscribers of Mutable are called whenever
* it's setter is triggered, even if the new data
* is the same as the old.
*/
function Mutable(initial = {}, recursive = false) {
const handler = {
$subscriptions: [],
$recursive: recursive,
$handler: undefined,
// Add a subscriber-function
$subscribe(callback) {
const id = this.$subscriptions.length;
this.$subscriptions.push({
callback,
id
});
return id;
},
// Remove a subscriber by it's id
$unsubscribe(id) {
const index = this
.$subscriptions
.findIndex((subscription) => (subscription.index === id));
this.$subscriptions[index] = null;
},
// Call all subscriptions
$callSubscriptions() {
this.$subscriptions.map((subscription) => {
subscription.callback();
});
},
// Convert all sub-objects of an object to State-proxies
$convertSubObjectsToState(object, recursive = false) {
if (typeof object !== "object" || object === null) {
return object;
}
const newObject = {};
Object.keys(object).map((key) => {
newObject[key] = this.$convertSubObjectsToState(object[key], true);
newObject[key].$isProxy = true;
});
if (recursive === true) {
return Mutable(newObject);
}
return newObject;
},
// Handling get
set(obj, prop, value) {
obj[prop] = this.$convertSubObjectsToState(value, this.$recursive);
this.$callSubscriptions();
return true;
},
// Handling set
get(obj, prop) {
if (prop[0] === "$") {
return this[prop];
}
return obj[prop];
}
};
return new Proxy(
recursive ? handler.$convertSubObjectsToState(initial) : initial,
handler
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment