store.js 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. import { useState, useEffect } from "react";
  2. const shallowEq = (x, y) => x === y;
  3. export const consoleMonitor = (key, original) => {
  4. let val = original;
  5. console.log(`Initializing ${key} with ${JSON.stringify(original)}`);
  6. return newVal => {
  7. console.log(`Updating ${key} from ${JSON.stringify(val)} to ${JSON.stringify(newVal)}`);
  8. }
  9. }
  10. export const createStore = (initial, monitor = null) => {
  11. const get = {};
  12. const update = {};
  13. const hooks = {};
  14. const mergeState = newState => Object.entries(newState).forEach(([key, value]) => {
  15. const setter = update[key];
  16. if (setter) {
  17. setter(value);
  18. } else {
  19. let _val = value;
  20. let _listeners = monitor === null ? [] : [{ callback: monitor(key, value), equality: shallowEq }];
  21. get[key] = () => _val;
  22. update[key] = newValue => {
  23. const oldValue = _val;
  24. _val = newValue;
  25. _listeners
  26. .filter(({ equality }) => !equality(oldValue, newValue))
  27. .forEach(({ callback }) => callback(newValue));
  28. }
  29. const hookName = "use" + key.charAt(0).toUpperCase() + key.slice(1);
  30. // name allows linters to pick this up as a hook
  31. const useValue = (equality = shallowEq) => {
  32. const [val, callback] = useState(_val);
  33. useEffect(() => {
  34. _listeners.push({ callback, equality });
  35. return () => {
  36. _listeners = _listeners.filter(ln => ln.callback !== callback);
  37. }
  38. }, [equality]);
  39. return val;
  40. }
  41. hooks[hookName] = useValue;
  42. }
  43. });
  44. mergeState(initial);
  45. return [hooks, mergeState, get];
  46. }