resso
Version:
The simplest React state manager
101 lines (97 loc) • 3.1 kB
JavaScript
;
var shim = require('use-sync-external-store/shim');
const __DEV__ = process.env.NODE_ENV !== 'production';
const isObj = (val) => Object.prototype.toString.call(val) === '[object Object]';
let run = (fn) => {
fn();
};
const resso = (data) => {
if (__DEV__ && !isObj(data)) {
throw new Error('object required');
}
const state = {};
const actions = {};
Object.keys(data).forEach((key) => {
const initVal = data[key];
// actions
if (typeof initVal === 'function') {
actions[key] = initVal;
return;
}
// state
const setters = new Set();
state[key] = {
subscribe: (setter) => {
setters.add(setter);
return () => setters.delete(setter);
},
getSnapshot: () => data[key],
triggerUpdate: () => setters.forEach((setter) => setter()),
};
});
const setKey = (key, val) => {
if (key in state) {
const newVal = val instanceof Function ? val(data[key]) : val;
if (data[key] !== newVal) {
data[key] = newVal;
run(() => state[key].triggerUpdate());
}
return;
}
if (__DEV__ && key in actions) {
throw new Error(`\`${key}\` is an action, can not update`);
}
if (__DEV__) {
throw new Error(`\`${key}\` is not initialized in store`);
}
};
return new Proxy(Object.assign(() => undefined, data), {
get: (_target, key) => {
if (key in actions) {
return actions[key];
}
if (key in state) {
try {
return shim.useSyncExternalStore(state[key].subscribe, state[key].getSnapshot, state[key].getSnapshot);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}
catch (err) {
return data[key];
}
}
if (__DEV__) {
throw new Error(`\`${key}\` is not initialized in store`);
}
},
set: (_target, key, val) => {
setKey(key, val);
return true;
},
apply: (_target, _thisArg, [key, updater]) => {
// store('key', val)
if (typeof key === 'string') {
setKey(key, updater);
return;
}
// store({ key: val })
if (isObj(key)) {
const newData = key;
Object.keys(newData).forEach((k) => {
setKey(k, newData[k]);
});
return;
}
// store(prev => next)
if (typeof key === 'function') {
const newData = key(data);
Object.keys(newData).forEach((k) => {
setKey(k, newData[k]);
});
}
},
});
};
resso.config = ({ batch }) => {
run = batch;
};
module.exports = resso;