@restate/core
Version:
_Restate_ is a predictable, easy to use, easy to integrate, typesafe state container for [React](https://reactjs.org/).
77 lines (76 loc) • 2.46 kB
JavaScript
import { current } from "immer";
import { useContext } from "react";
import { isFunction } from "./utils";
function createNextHook(provider, scope) {
function useNextHook(selector, trace) {
const store = useContext(provider);
const outerSelector = scope ? scope : (state) => state;
function updateNestedState(subState, nextValue) {
const proxy = getProxy({ subState, selector, outerSelector });
mutateNestedObject({ proxy, nextValue });
return subState;
}
function updateState(updateFunctionOrNextState) {
return store.next((currentState) => {
if (isFunction(updateFunctionOrNextState)) {
const subState = selector(outerSelector(currentState));
return updateFunctionOrNextState(subState);
} else {
const nextValue = updateFunctionOrNextState;
return updateNestedState(currentState, nextValue);
}
}, trace);
}
return updateState;
}
return useNextHook;
}
function getProxy(props) {
const { subState, selector, outerSelector } = props;
const unfrozenSupState = Object.isFrozen(subState) ? Object.assign({}, current(subState)) : subState;
let path = [];
const proxyAccess = (parent) => ({
get(target2, key) {
if (Array.isArray(target2) && isNaN(key)) {
return target2[key];
}
if (key === "___restate_parent___") {
return parent;
}
path.push({
parent,
target: target2,
key,
idx: Array.isArray(parent) ? parent.indexOf(target2) : -1
});
if (typeof target2[key] === "object" && target2[key] !== null) {
return new Proxy(target2[key], proxyAccess(target2));
} else if (Array.isArray(target2) && !isNaN(key)) {
return new Proxy(target2[key], proxyAccess(target2));
} else {
return target2[key];
}
}
});
const proxy = new Proxy(unfrozenSupState, proxyAccess(unfrozenSupState));
const target = selector(outerSelector(proxy));
if (Array.isArray(target.___restate_parent___)) {
return {
...path.at(-1),
isArray: true
};
}
return { ...path.at(-1), isArray: false, idx: -1 };
}
function mutateNestedObject(props) {
const { proxy, nextValue } = props;
const { target, key, isArray, idx, parent } = proxy;
if (isArray && typeof parent[idx] === typeof nextValue) {
parent[idx] = nextValue;
} else {
target[key] = nextValue;
}
}
export {
createNextHook
};