get-set-action
Version:
A React State Management Library similar and alternative to mobx, redux.etc. it's the simplest, smallest and fastest state management library for react with dev tools support.
471 lines (466 loc) • 13.5 kB
JavaScript
;
var react = require('react');
function ref(value) {
let current = value;
return function (newValue) {
if (arguments.length === 0) {
return current;
}
else {
current = newValue;
}
};
}
function fnRef(fn) {
const current = ref(fn);
function returnedFn(...args) {
var _a, _b;
// @ts-ignore
return ((_b = (_a = current === null || current === void 0 ? void 0 : current()) === null || _a === void 0 ? void 0 : _a.call) === null || _b === void 0 ? void 0 : _b.call(_a, this, ...args)) || null;
}
returnedFn.set = (fn) => {
current(fn);
};
return returnedFn;
}
function debounce(func, wait, setStatus) {
let timeout;
return function (...args) {
clearTimeout(timeout);
setStatus === null || setStatus === void 0 ? void 0 : setStatus("loading");
// @ts-ignore
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
function throttle(func, limit, setStatus) {
let inThrottle;
return function (...args) {
if (!inThrottle) {
// @ts-ignore
func.apply(this, args);
inThrottle = true;
setStatus === null || setStatus === void 0 ? void 0 : setStatus("loading");
setTimeout(() => (inThrottle = false), limit);
}
};
}
function asyncAction(
// p is the props type
fn, options = {}) {
let data = null;
let error = null;
let status = "intial";
let interceptorAfter = null;
let interceptorError = null;
let isDebounced = false;
let isDebouncing = false;
let abortController = new AbortController();
let subscribers = [];
let successSubscriber = [];
let errorSubscriber = [];
let stateSubscriber = [];
let isThrottled = false;
let payload = {};
const onSuccess = (subscriber) => {
successSubscriber.push(subscriber);
return (subscriber) => {
successSubscriber = successSubscriber.filter((s) => s !== subscriber);
};
};
const onError = (subscriber) => {
errorSubscriber.push(subscriber);
return (subscriber) => {
errorSubscriber = errorSubscriber.filter((s) => s !== subscriber);
};
};
const onRun = (subscriber) => {
subscribers.push(subscriber);
return (subscriber) => {
subscribers = subscribers.filter((s) => s !== subscriber);
};
};
const onStateChange = (subscriber) => {
stateSubscriber.push(subscriber);
return (subscriber) => {
stateSubscriber = stateSubscriber.filter((s) => s !== subscriber);
};
};
const callSubs = debounce((subscriber) => {
subscribers.forEach((s) => s(subscriber));
}, 0);
const callSuccessSubs = debounce((subscriber) => {
successSubscriber.forEach((s) => s(subscriber));
}, 0);
const callErrorSubs = debounce((subscriber) => {
errorSubscriber.forEach((s) => s(subscriber));
}, 0);
const callStateChangeSubs = debounce((subscriber) => {
buildState();
stateSubscriber.forEach((s) => s(subscriber));
}, 0);
const execute = async function (_payload = {}) {
payload = _payload;
if (interceptor) {
try {
payload = await interceptor(payload);
}
catch (err) {
console.error("error in interceptor", err);
}
}
let _data = null;
let _error = null;
abortController.abort();
setStatus("loading");
try {
abortController = new AbortController();
// @ts-ignore
_data = await fn.call(this, payload, abortController.signal);
if (interceptorAfter) {
try {
_data = await interceptorAfter(_data);
}
catch (err) {
console.error("error in interceptorAfter", err);
}
}
setStatus("success");
}
catch (err) {
if (err instanceof Error && err.name === "AbortError") {
setStatus("cancelled");
return null;
}
_error = err;
if (interceptorError) {
try {
_error = await interceptorError(_error);
}
catch (err) {
console.error("error in interceptorError", err);
}
}
setStatus("error");
}
finally {
callSubs();
if (status === "success") {
setData(_data);
setError(null);
callSuccessSubs();
}
if (status === "error") {
setError(_error);
setData(null);
callErrorSubs();
}
_data = null;
_error = null;
}
return data;
};
const abort = () => {
abortController.abort();
};
let currentPage = 1;
let countPerPage = 10;
const setPage = (page) => {
currentPage = page;
callStateChangeSubs();
};
const setCountPerPage = (count) => {
countPerPage = count;
callStateChangeSubs();
};
let extraState = {};
const setExtraState = (state) => {
extraState = state;
callStateChangeSubs();
};
let filters = {};
const setFilters = (filters) => {
callStateChangeSubs();
};
const setData = (_data) => {
data = _data;
callStateChangeSubs();
};
const setError = (_error) => {
error = _error;
callStateChangeSubs();
};
const setStatus = (_status) => {
status = _status;
callStateChangeSubs();
};
let run = execute;
if (options.debounce) {
run = debounce(execute, options.debounce, setStatus);
isDebounced = true;
}
if (options.throttle) {
run = throttle(execute, options.throttle, setStatus);
isThrottled = true;
}
let interceptor = null;
let state = {};
const buildState = () => {
state = {
get data() {
return data;
},
run,
abort,
get error() {
return error;
},
get isRunning() {
return status === "loading";
},
get isSuccess() {
return status === "success";
},
get isError() {
return status === "error";
},
get isDebounced() {
return isDebounced;
},
get isThrottled() {
return isThrottled;
},
get isDebouncing() {
return isDebouncing;
},
get status() {
return status;
},
get abortController() {
return abortController;
},
get currentPage() {
return currentPage;
},
get countPerPage() {
return countPerPage;
},
get extraState() {
return extraState;
},
get filters() {
return filters;
},
get payload() {
return payload;
},
setData,
setError,
setPage,
setCountPerPage,
setExtraState,
setFilters,
setStatus,
};
};
buildState();
let returnVal = {
run,
abort,
getState: () => state,
onRun,
intercept(_interceptor) {
interceptor = _interceptor;
},
interceptAfter(_interceptor) {
interceptorAfter = _interceptor;
},
interceptError(_interceptor) {
interceptorError = _interceptor;
},
onSuccess,
onError,
subscribe: onStateChange,
get data() {
return data;
},
get error() {
return error;
},
get isRunning() {
return status === "loading";
},
get isSuccess() {
return status === "success";
},
get isError() {
return status === "error";
},
get isDebounced() {
return isDebounced;
},
get isThrottled() {
return isThrottled;
},
get isDebouncing() {
return isDebouncing;
},
get status() {
return status;
},
get abortController() {
return abortController;
},
get currentPage() {
return currentPage;
},
get countPerPage() {
return countPerPage;
},
get extraState() {
return extraState;
},
get filters() {
return filters;
},
get payload() {
return payload;
},
setData,
setError,
setPage,
setCountPerPage,
setExtraState,
setFilters,
setStatus,
};
return returnVal;
}
function useAction(action) {
const [actionData, setActionData] = react.useState(action.getState());
react.useEffect(() => {
action.subscribe(() => {
setActionData(action.getState());
});
}, [setActionData]);
return actionData;
}
function useOnMount(callback) {
// Implement logic to run callback on component mount
react.useEffect(() => {
callback();
}, []);
}
function useOnRun(callback, deps) {
// Implement logic to run callback after action is called
const callbackRef = react.useRef(callback);
callbackRef.current = callback;
react.useEffect(() => {
deps.forEach((action) => {
action.onRun(() => callbackRef.current());
});
}, []);
}
function useOnSuccess(callback, deps) {
// Implement logic to run callback on success
const callbackRef = react.useRef(callback);
callbackRef.current = callback;
react.useEffect(() => {
deps.forEach((action) => {
action.onSuccess(() => callbackRef.current());
});
}, []);
}
function useOnError(callback, deps) {
// Implement logic to run callback on error
const callbackRef = react.useRef(callback);
callbackRef.current = callback;
react.useEffect(() => {
deps.forEach((action) => {
action.onError(() => callbackRef.current());
});
}, []);
}
function action(fn) {
let state;
let subscribers = [];
let interceptor = null;
// Create the enhanced function
let forward;
let forwardInProgress = false;
let _props = {};
const enhancedFn = function (props) {
_props = props;
enhancedFn.callCountTotal++;
if (forward && !forwardInProgress) {
forwardInProgress = true;
forward(_props);
forwardInProgress = false;
}
// Check for interceptor
else {
if (interceptor) {
_props = interceptor(_props);
}
state = fn(_props);
forwardInProgress = false;
enhancedFn.callCount++;
}
// Notify subscribers
subscribers.forEach((callback) => callback(state));
return state;
};
enhancedFn.callCount = 0;
enhancedFn.callCountTotal = 0;
// Add properties
enhancedFn.intercept = (_interceptor) => {
interceptor = _interceptor;
};
enhancedFn.forwardCall = (callback) => {
forward = callback;
};
enhancedFn.subscribe = (callback) => {
subscribers.push(callback);
return () => {
subscribers = subscribers.filter((cb) => cb !== callback);
};
};
enhancedFn.retry = () => {
enhancedFn(_props);
};
enhancedFn.getState = () => state;
return enhancedFn;
}
const interceptorFetch = fnRef((payload) => payload);
const interceptorFetchSuccess = fnRef((data) => data);
const interceptorFetchError = fnRef((error) => error);
function Fetch(options = { debounce: 1000 }) {
const action = asyncAction(async (payload, signal) => {
return fetch(payload.url, Object.assign({ signal }, payload)).then((res) => {
var _a;
if (res.ok &&
((_a = res.headers.get("content-type")) === null || _a === void 0 ? void 0 : _a.includes("application/json"))) {
return res.json();
}
return res;
});
}, options);
//@ts-ignore
action.intercept(interceptorFetch);
//@ts-ignore
action.interceptAfter(interceptorFetchSuccess);
//@ts-ignore
action.interceptError(interceptorFetchError);
return action;
}
exports.Fetch = Fetch;
exports.action = action;
exports.asyncAction = asyncAction;
exports.interceptorFetch = interceptorFetch;
exports.interceptorFetchError = interceptorFetchError;
exports.interceptorFetchSuccess = interceptorFetchSuccess;
exports.useAction = useAction;
exports.useOnError = useOnError;
exports.useOnMount = useOnMount;
exports.useOnRun = useOnRun;
exports.useOnSuccess = useOnSuccess;
//# sourceMappingURL=index.js.map