JSFiddle - React, Tailwind, and code Playground
by dp0ch
JavaScript
// UTIL
const ON_CHANGE = Symbol('ON_CHANGE');
function reactiveProps(obj, props) {
// initializing events for properties
obj[ON_CHANGE] = Object.assign(
obj[ON_CHANGE] || {},
Object.keys(props)
.filter((key) => !obj[key])
.reduce((acc, key) => Object.assign(acc, {[key]: Event()}), {})
);
return Object.defineProperties(
obj,
Object.entries(props)
.reduce((acc, [key, value]) => Object.assign(acc, {
[key] : {
get: () => value,
set: (val) => {
const oldVal = value;
value = val;
if(val !== oldVal) obj[ON_CHANGE][key].emit(val, oldVal)
return value;
}
}
}), {})
);
}
// just an accessor for the onChange symbol
function onChange(key, {
[ON_CHANGE]: {
[key]: event
} = {}
}) {
if(!event) throw new Error(`No onChange event defined for ${key}`);
return event;
}
function Event() {
let handlers = {};
let nextId = 0;
function on(handler) {
const id = nextId++;
handlers[id] = handler;
return () => delete handlers[id];
}
function emit(...args) {
Object.values(handlers).forEach((f) => {
try {
f(...args);
} catch (err) {
console.error(err);
}
});
}
function once(handler) {
const off = on((...args) => {
handler(...args);
off();
});
return off;
}
function clear() {
handlers = {};
}
return {
on,
emit,
once,
clear,
get listeners() {
return Object.values(handlers);
},
};
}
// EXAMPLE
const leaderboard = reactiveProps({}, {visible: true});
const sidePanel = reactiveProps({}, {visible: false});
const popup = reactiveProps({}, {visible: false});
const updateLeaderboardVisibility = () => leaderboard.visible = !sidePanel.visible && !popup.visible;
onChange('visible', sidePanel).on(updateLeaderboardVisibility);
onChange('visible', popup).on(updateLeaderboardVisibility);
// TEST
onChange('visible',...