UNPKG

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
'use strict'; 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