import { useState, useEffect } from "react"; const shallowEq = (x, y) => x === y; export const consoleMonitor = (key, original) => { let val = original; console.log(`Initializing ${key} with ${JSON.stringify(original)}`); return newVal => { console.log(`Updating ${key} from ${JSON.stringify(val)} to ${JSON.stringify(newVal)}`); } } export const createStore = (initial, monitor = null) => { const get = {}; const update = {}; const hooks = {}; const mergeState = newState => Object.entries(newState).forEach(([key, value]) => { const setter = update[key]; if (setter) { setter(value); } else { let _val = value; let _listeners = monitor === null ? [] : [{ callback: monitor(key, value), equality: shallowEq }]; get[key] = () => _val; update[key] = newValue => { const oldValue = _val; _val = newValue; _listeners .filter(({ equality }) => !equality(oldValue, newValue)) .forEach(({ callback }) => callback(newValue)); } const hookName = "use" + key.charAt(0).toUpperCase() + key.slice(1); // name allows linters to pick this up as a hook const useValue = (equality = shallowEq) => { const [val, callback] = useState(_val); useEffect(() => { _listeners.push({ callback, equality }); return () => { _listeners = _listeners.filter(ln => ln.callback !== callback); } }, [equality]); return val; } hooks[hookName] = useValue; } }); mergeState(initial); return [hooks, mergeState, get]; }