123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263 |
- import { useState, useEffect } from "react";
- const createObservable = initial => {
- let _val = initial;
- let _listeners = [];
- return {
- _get: () => _val,
- _set: newValue => {
- const oldValue = _val;
- _val = newValue;
- _listeners
- .filter(({ equality }) => !equality(oldValue, newValue))
- .forEach(({ callback }) => callback(newValue));
- },
- _sub: (callback, equality) => {
- _listeners.push({ callback, equality });
- return () => {
- _listeners = _listeners.filter(ln => ln.callback !== callback);
- }
- }
- }
- }
- const nameToHookName = name => "use" + name.chatAt(0).toUpperCase() + name.slice(1);
- const shallowEq = (x, y) => x === y;
- const createHook = obs => {
- // allow linters to pick this up as a hook
- const useObservable = (equality = shallowEq) => {
- const [val, setVal] = useState(obs._get());
- useEffect(() => obs._sub(setVal, equality), [equality]);
- return val;
- }
- return useObservable;
- }
- export const createStore = (initial, actions = {}) => {
- const store = {}; // maps keys to observables
- const hooks = {};
- const mergeState = newState => {
- Object.entries(newState).forEach(([key, newValue]) => {
- const obs = store[key];
- if (obs) {
- obs._set(newValue);
- } else {
- const newObs = createObservable(newValue);
- store[key] = newObs;
- hooks[nameToHookName(key)] = createHook(newObs);
- }
- });
- };
- mergeState(initial);
- const dispatch = Object.fromEntries(Object.entries(actions).map(([name, action]) => [name, (...args) => mergeState(action(...args))]));
- const selector = Object.fromEntries(Object.entries(store).map(([key, obs]) => [key, obs._get]));
- return [hooks, dispatch, selector];
- }
|