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',...