|
@@ -1,11 +1,7 @@
|
|
|
import { useState, useEffect } from "react";
|
|
|
|
|
|
-const nameToHookName = name => "use" + name.charAt(0).toUpperCase() + name.slice(1);
|
|
|
-
|
|
|
const shallowEq = (x, y) => x === y;
|
|
|
|
|
|
-const mapValues = (src, fn) => Object.fromEntries(Object.entries(src).map(([key, val]) => [key, fn(val)]));
|
|
|
-
|
|
|
const createObservable = initial => {
|
|
|
let _val = initial;
|
|
|
let _listeners = [];
|
|
@@ -19,42 +15,37 @@ const createObservable = initial => {
|
|
|
.filter(({ equality }) => !equality(oldValue, newValue))
|
|
|
.forEach(({ callback }) => callback(newValue));
|
|
|
},
|
|
|
- _sub: (callback, equality) => {
|
|
|
- _listeners.push({ callback, equality });
|
|
|
- return () => {
|
|
|
- _listeners = _listeners.filter(ln => ln.callback !== callback);
|
|
|
- }
|
|
|
- }
|
|
|
};
|
|
|
|
|
|
// name allows linters to pick this up as a hook
|
|
|
const useValue = (equality = shallowEq) => {
|
|
|
- const [val, setVal] = useState(_val);
|
|
|
- useEffect(() => obs._sub(setVal, equality), [equality]);
|
|
|
+ const [val, callback] = useState(_val);
|
|
|
+ useEffect(() => {
|
|
|
+ _listeners.push({ callback, equality });
|
|
|
+ return () => {
|
|
|
+ _listeners = _listeners.filter(ln => ln.callback !== callback);
|
|
|
+ }
|
|
|
+ }, [equality]);
|
|
|
return val;
|
|
|
}
|
|
|
|
|
|
return [obs, useValue];
|
|
|
}
|
|
|
|
|
|
-const mergeState = (store, hooks, newState) => {
|
|
|
- Object.entries(newState).forEach(([key, newValue]) => {
|
|
|
- const obs = store[key];
|
|
|
- if (obs) {
|
|
|
- obs._set(newValue);
|
|
|
- } else {
|
|
|
- const [obs, hook] = createObservable(newValue);
|
|
|
- store[key] = obs;
|
|
|
- hooks[nameToHookName(key)] = hook;
|
|
|
- }
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
export const createStore = (initial, actions = {}) => {
|
|
|
const store = {};
|
|
|
const hooks = {};
|
|
|
- mergeState(store, hooks, initial);
|
|
|
- const dispatch = mapValues(actions, act => (...args) => mergeState(store, hooks, act(...args)));
|
|
|
- const selector = mapValues(store, obs => obs._get);
|
|
|
+ const dispatch = {};
|
|
|
+ const selector = {};
|
|
|
+ Object.entries(initial).forEach(([key, value]) => {
|
|
|
+ const [obs, hook] = createObservable(value);
|
|
|
+ const hookName = "use" + key.charAt(0).toUpperCase() + key.slice(1);
|
|
|
+ store[key] = obs;
|
|
|
+ hooks[hookName] = hook;
|
|
|
+ });
|
|
|
+ Object.entries(actions).forEach(([name, act]) => {
|
|
|
+ dispatch[name] = (...args) => Object.entries(act(...args)).forEach(([key, newValue]) => store[key]?._set(newValue))
|
|
|
+ });
|
|
|
+ Object.entries(store).forEach(([key, obs]) => { selector[key] = obs._get });
|
|
|
return [hooks, dispatch, selector];
|
|
|
}
|