UNPKG

prostgles-client

Version:

Reactive client for Postgres

302 lines (301 loc) 11.1 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.__prglReactInstalled = exports.useFetch = exports.useSync = exports.useSubscribe = exports.usePromise = exports.useIsMounted = exports.useEffectAsync = exports.useAsyncEffectQueue = exports.useEffectDeep = exports.useMemoDeep = exports.useDeepCompareMemoize = exports.getIO = exports.getReact = void 0; const prostgles_types_1 = require("prostgles-types"); let React; const alertNoReact = (...args) => { throw "Must install react"; }; const alertNoReactT = (...args) => { throw "Must install react"; }; const getReact = (throwError) => { try { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition React !== null && React !== void 0 ? React : (React = require("react")); } catch (err) { } // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (throwError && !React) throw new Error("Must install react"); return React; }; exports.getReact = getReact; (0, exports.getReact)(); const { useEffect = alertNoReact, useCallback = alertNoReact, useRef, useState = alertNoReactT, // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition } = (_a = React) !== null && _a !== void 0 ? _a : {}; const getIO = (throwError = false) => { try { const io = require("socket.io-client"); return io; } catch (err) { } if (throwError) throw new Error("Must install socket.io-client"); return {}; }; exports.getIO = getIO; const useDeepCompareMemoize = (value) => { const ref = useRef(); if (!(0, prostgles_types_1.isEqual)(value, ref.current)) { ref.current = value; } return ref.current; }; exports.useDeepCompareMemoize = useDeepCompareMemoize; exports.useMemoDeep = ((callback, deps) => { // eslint-disable-next-line react-hooks/exhaustive-deps return React.useMemo(callback, deps === null || deps === void 0 ? void 0 : deps.map(exports.useDeepCompareMemoize)); }); exports.useEffectDeep = ((callback, deps) => { // eslint-disable-next-line react-hooks/exhaustive-deps useEffect(callback, deps === null || deps === void 0 ? void 0 : deps.map(exports.useDeepCompareMemoize)); }); /** * Debounce with execute first * Used to ensure subscriptions are always cleaned up */ const useAsyncEffectQueue = (effect, deps) => { const newEffect = useRef(); const activeEffect = useRef(); const onRender = async () => { var _a; /** * Await and cleanup previous effect * */ if (((_a = activeEffect.current) === null || _a === void 0 ? void 0 : _a.state) === "resolved") { const { cleanup, effect } = activeEffect.current; activeEffect.current = { state: "cleaning", effect }; await cleanup().catch(console.error); activeEffect.current = undefined; } /** * Start new effect */ if (newEffect.current && !activeEffect.current) { const currentEffect = newEffect.current; const { effect } = currentEffect; activeEffect.current = { state: "resolving", effect }; const cleanup = await effect() .then((run) => { /** * Wrapped in a promise to ensure cleanup is awaited */ return async () => { await (run === null || run === void 0 ? void 0 : run()); }; }) .catch((e) => { console.error(e); return async () => { }; }); activeEffect.current = { state: "resolved", effect, cleanup }; if (currentEffect !== newEffect.current) { onRender(); } } }; (0, exports.useEffectDeep)(() => { newEffect.current = { effect, deps }; onRender(); return () => { newEffect.current = undefined; onRender(); }; }, deps); }; exports.useAsyncEffectQueue = useAsyncEffectQueue; const useEffectAsync = (effect, inputs) => { const onCleanup = useRef({ cleanup: undefined, effect, cleanupEffect: undefined, }); onCleanup.current.effect = effect; (0, exports.useEffectDeep)(() => { effect().then((result) => { if (typeof result === "function") { onCleanup.current.cleanup = result; if (onCleanup.current.cleanupEffect === effect) { result(); } } }); return () => { var _a, _b; onCleanup.current.cleanupEffect = effect; (_b = (_a = onCleanup.current).cleanup) === null || _b === void 0 ? void 0 : _b.call(_a); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, inputs); }; exports.useEffectAsync = useEffectAsync; const useIsMounted = () => { const isMountedRef = useRef(true); const isMounted = useCallback(() => isMountedRef.current, []); useEffect(() => { /** React 18 Strict Mode fix (new strict mode restores the previous state on the second mount) */ isMountedRef.current = true; return () => { isMountedRef.current = false; }; }, []); return isMounted; }; exports.useIsMounted = useIsMounted; const usePromise = (f, deps = []) => { const isPromiseFunc = (val) => { try { return typeof val === "function" || val instanceof Promise; } catch (e) { console.error(e); } return false; }; const isNamedObj = (val) => { try { return (0, prostgles_types_1.isObject)(val) && !isPromiseFunc(val); } catch (e) { console.error(e); } return false; }; const getNamedObjResults = async (val) => { const data = {}; const keys = (0, prostgles_types_1.getKeys)(val); for await (const key of keys) { const prop = val[key]; try { data[key] = typeof prop === "function" ? await prop() : await prop; } catch (e) { console.error(e); } } return data; }; const [result, setResult] = useState(isNamedObj(f) ? {} : undefined); const getIsMounted = (0, exports.useIsMounted)(); (0, exports.useAsyncEffectQueue)(async () => { let promiseResult; try { if (isNamedObj(f)) { promiseResult = await getNamedObjResults(f); } else { const funcRes = await f(); const isNObj = isNamedObj(funcRes); promiseResult = isNObj ? await getNamedObjResults(funcRes) : isPromiseFunc(funcRes) ? await funcRes() : funcRes; } } catch (e) { console.error(e); } if (!getIsMounted()) return; setResult(promiseResult); }, deps); return result; }; exports.usePromise = usePromise; const useSubscribe = (subFunc, expectsOne, filter, options, hookOptions) => { const { skip } = hookOptions !== null && hookOptions !== void 0 ? hookOptions : {}; const defaultLoadingResult = { data: undefined, error: undefined, isLoading: true }; const [{ data, isLoading, error }, setHookResult] = useState(defaultLoadingResult); const hookResult = { data, isLoading, error }; const hookResultRef = useRef(hookResult); hookResultRef.current = hookResult; const getIsMounted = (0, exports.useIsMounted)(); (0, exports.useAsyncEffectQueue)(async () => { if (!getIsMounted() || skip) return; if (!(0, prostgles_types_1.isEqual)(hookResultRef.current, defaultLoadingResult)) { setHookResult(defaultLoadingResult); } const setError = (newError) => { if (!getIsMounted()) return; setHookResult({ data: undefined, error: newError, isLoading: false }); }; try { const sub = await subFunc(filter, options, (newData) => { if (!getIsMounted()) return; setHookResult({ data: expectsOne ? newData[0] : newData, error: undefined, isLoading: false, }); }, setError); return () => { sub.unsubscribe(); }; } catch (error) { setError(error); } }, [subFunc, filter, options, skip]); return { data, error, isLoading }; }; exports.useSubscribe = useSubscribe; const useSync = (syncFunc, basicFilter, syncOptions, hookOptions) => { const { skip } = hookOptions !== null && hookOptions !== void 0 ? hookOptions : {}; const defaultLoadingResult = { data: undefined, error: undefined, isLoading: true }; const [{ data, error, isLoading }, setResult] = useState(defaultLoadingResult); const getIsMounted = (0, exports.useIsMounted)(); (0, exports.useAsyncEffectQueue)(async () => { if (!getIsMounted() || skip) return; const setError = (newError) => { if (!getIsMounted()) return; setResult({ data: undefined, error: newError, isLoading: false }); }; try { const syncHandlers = await syncFunc(basicFilter, syncOptions, (newData) => { if (!getIsMounted()) return; setResult({ data: newData, error: undefined, isLoading: false }); }, setError); return syncHandlers.$unsync; } catch (error) { setError(error); } }, [syncFunc, basicFilter, syncOptions, skip]); return { data, error, isLoading }; }; exports.useSync = useSync; const useFetch = (fetchFunc, args = [], hookOptions) => { const { skip, deps = [] } = hookOptions !== null && hookOptions !== void 0 ? hookOptions : {}; const defaultLoadingResult = { data: undefined, error: undefined, isLoading: true }; const [{ data, error, isLoading }, setResult] = useState(defaultLoadingResult); const getIsMounted = (0, exports.useIsMounted)(); (0, exports.useAsyncEffectQueue)(async () => { if (!getIsMounted() || skip) return; setResult(defaultLoadingResult); try { const newData = await fetchFunc(...args); if (!getIsMounted()) return; setResult({ data: newData, error: undefined, isLoading: false }); } catch (error) { if (!getIsMounted()) return; setResult({ data: undefined, error, isLoading: false }); } }, [...args, ...deps]); return { data, error, isLoading }; }; exports.useFetch = useFetch; const __prglReactInstalled = () => Boolean(React && useRef); exports.__prglReactInstalled = __prglReactInstalled;