UNPKG

@hazae41/glacier

Version:

Yet another React data (re)fetching library

298 lines (294 loc) 11.9 kB
'use strict'; var option = require('@hazae41/option'); var result = require('@hazae41/result'); var arrays = require('../../../../libs/arrays/arrays.cjs'); var ref = require('../../../../libs/react/ref.cjs'); var index = require('../../../../libs/request/index.cjs'); var index$1 = require('../../../../libs/signals/index.cjs'); var time = require('../../../../libs/time/time.cjs'); var core = require('../../../core/core.cjs'); var helper = require('../../../queries/scroll/helper.cjs'); var react = require('react'); function useScrollableQuery(factory, deps) { const query = react.useMemo(() => { return factory(...deps); }, deps); if (query == null) return useSkeletonScrollableQuery(); if (query.settings.fetcher == null) return useFetcherlessScrollableQuery(query.settings); return useFetcherfulScrollableQuery(query.settings); } function useSkeletonScrollableQuery() { ref.useRenderRef(undefined); const cacheKey = react.useMemo(() => { // NOOP }, [undefined]); react.useState(0); react.useRef(); react.useRef(); react.useMemo(() => { // NOOP }, [cacheKey]); react.useCallback(() => { // NOOP }, [cacheKey]); react.useCallback(() => { // NOOP }, [cacheKey]); react.useEffect(() => { // NOOP }, [cacheKey]); react.useEffect(() => { // NOOP }, [cacheKey]); const mutateOrThrow = react.useCallback(async (mutator) => { throw new core.MissingKeyError(); }, [cacheKey]); const deleteOrThrow = react.useCallback(async () => { throw new core.MissingKeyError(); }, [cacheKey]); const fetchOrThrow = react.useCallback(async (init) => { throw new core.MissingKeyError(); }, [cacheKey]); const refetchOrThrow = react.useCallback(async (init) => { throw new core.MissingKeyError(); }, [cacheKey]); const scrollOrThrow = react.useCallback(async (init) => { throw new core.MissingKeyError(); }, [cacheKey]); const peekOrNull = react.useCallback(() => { return undefined; }, [undefined, undefined]); return { mutateOrThrow, deleteOrThrow, fetchOrThrow, refetchOrThrow, scrollOrThrow, peekOrNull }; } /** * Scroll query * @param scroller * @param fetcher * @param settings * @returns */ function useFetcherlessScrollableQuery(settings) { const settingsRef = ref.useRenderRef(settings); const cacheKey = react.useMemo(() => { return helper.Scrollable.getCacheKey(settings.key); }, [settings.key]); const [, setCounter] = react.useState(0); const stateRef = react.useRef(); const aborterRef = react.useRef(); react.useMemo(() => { stateRef.current = core.core.getStateSync(cacheKey); aborterRef.current = core.core.getAborterSync(cacheKey); }, [cacheKey]); const setState = react.useCallback((state) => { stateRef.current = state; setCounter(c => c + 1); }, [cacheKey]); const setAborter = react.useCallback((aborter) => { aborterRef.current = aborter; setCounter(c => c + 1); }, [cacheKey]); react.useEffect(() => { if (stateRef.current != null) return; core.core.getOrThrow(cacheKey, settingsRef.current).then(setState).catch(console.warn); }, [cacheKey]); react.useEffect(() => { const onState = () => { core.core.getOrThrow(cacheKey, settingsRef.current).then(setState).catch(console.warn); return new option.None(); }; const onAborter = () => { setAborter(core.core.getAborterSync(cacheKey)); return new option.None(); }; core.core.onState.on(cacheKey, onState, { passive: true }); core.core.onAborter.on(cacheKey, onAborter, { passive: true }); core.core.increment(cacheKey, settingsRef.current); return () => { core.core.decrementOrThrow(cacheKey, settingsRef.current); core.core.onState.off(cacheKey, onState); core.core.onAborter.off(cacheKey, onAborter); }; }, [cacheKey]); const mutateOrThrow = react.useCallback(async (mutator) => { return await core.core.mutateOrThrow(cacheKey, mutator, settingsRef.current); }, [cacheKey]); const deleteOrThrow = react.useCallback(async () => { return await core.core.deleteOrThrow(cacheKey, settingsRef.current); }, [cacheKey]); const fetchOrThrow = react.useCallback(async (aborter = new AbortController()) => { throw new core.MissingFetcherError(); }, [cacheKey]); const refetchOrThrow = react.useCallback(async (aborter = new AbortController()) => { throw new core.MissingFetcherError(); }, [cacheKey]); const scrollOrThrow = react.useCallback(async (aborter = new AbortController()) => { throw new core.MissingFetcherError(); }, [cacheKey]); const state = stateRef.current; const aborter = aborterRef.current; const ready = state != null; const fetching = aborter != null; const optimistic = state?.isFake(); const current = state?.current; const data = state?.data; const error = state?.error; const real = state?.real; const fake = state?.fake; const peekOrNull = react.useCallback(() => { const pages = state?.real?.data?.get(); if (pages == null) return undefined; return settings.scroller(arrays.Arrays.last(pages)); }, [state?.real?.data, settings.scroller]); return { ...settings, cacheKey, current, data, error, real, fake, ready, optimistic, aborter, fetching, mutateOrThrow, fetchOrThrow, refetchOrThrow, scrollOrThrow, deleteOrThrow, peekOrNull, }; } function useFetcherfulScrollableQuery(settings) { const settingsRef = ref.useRenderRef(settings); const cacheKey = react.useMemo(() => { return helper.Scrollable.getCacheKey(settings.key); }, [settings.key]); const [, setCounter] = react.useState(0); const stateRef = react.useRef(); const aborterRef = react.useRef(); react.useMemo(() => { stateRef.current = core.core.getStateSync(cacheKey); aborterRef.current = core.core.getAborterSync(cacheKey); }, [cacheKey]); const setState = react.useCallback((state) => { stateRef.current = state; setCounter(c => c + 1); }, [cacheKey]); const setAborter = react.useCallback((aborter) => { aborterRef.current = aborter; setCounter(c => c + 1); }, [cacheKey]); react.useEffect(() => { if (stateRef.current != null) return; core.core.getOrThrow(cacheKey, settingsRef.current).then(setState).catch(console.warn); }, [cacheKey]); react.useEffect(() => { const onState = () => { core.core.getOrThrow(cacheKey, settingsRef.current).then(setState).catch(console.warn); return new option.None(); }; const onAborter = () => { setAborter(core.core.getAborterSync(cacheKey)); return new option.None(); }; core.core.onState.on(cacheKey, onState, { passive: true }); core.core.onAborter.on(cacheKey, onAborter, { passive: true }); core.core.increment(cacheKey, settingsRef.current); return () => { core.core.decrementOrThrow(cacheKey, settingsRef.current); core.core.onState.off(cacheKey, onState); core.core.onAborter.off(cacheKey, onAborter); }; }, [cacheKey]); const mutateOrThrow = react.useCallback(async (mutator) => { return await core.core.mutateOrThrow(cacheKey, mutator, settingsRef.current); }, [cacheKey]); const deleteOrThrow = react.useCallback(async () => { return await core.core.deleteOrThrow(cacheKey, settingsRef.current); }, [cacheKey]); const fetchOrThrow = react.useCallback(async (init) => { const state = stateRef.current; const settings = settingsRef.current; if (index.shouldUseCacheIfFresh(init?.cache) && time.Time.isAfterNow(state?.real?.current.cooldown)) return new result.Err(state); if (index.shouldUseCacheIfStale(init?.cache) && time.Time.isAfterNow(state?.real?.current.expiration)) return new result.Err(state); if (!index.shouldUseNetwork(init?.cache)) throw new Error(`Could not fetch using the provided cache directive`); const aborter = new AbortController(); const signal = AbortSignal.any([aborter.signal, index$1.AbortSignals.getOrNever(init?.signal)]); return new result.Ok(await core.core.runOrJoin(cacheKey, aborter, () => helper.Scrollable.fetchOrThrow(cacheKey, signal, settings))); }, [cacheKey]); const refetchOrThrow = react.useCallback(async (init) => { const state = stateRef.current; const settings = settingsRef.current; if (index.shouldUseCacheIfFresh(init?.cache) && time.Time.isAfterNow(state?.real?.current.cooldown)) return new result.Err(state); if (index.shouldUseCacheIfStale(init?.cache) && time.Time.isAfterNow(state?.real?.current.expiration)) return new result.Err(state); if (!index.shouldUseNetwork(init?.cache)) throw new Error(`Could not fetch using the provided cache directive`); const aborter = new AbortController(); const signal = AbortSignal.any([aborter.signal, index$1.AbortSignals.getOrNever(init?.signal)]); return new result.Ok(await core.core.runOrReplace(cacheKey, aborter, () => helper.Scrollable.fetchOrThrow(cacheKey, signal, settings))); }, [cacheKey]); const scrollOrThrow = react.useCallback(async (init) => { const state = stateRef.current; const settings = settingsRef.current; if (index.shouldUseCacheIfFresh(init?.cache) && time.Time.isAfterNow(state?.real?.current.cooldown)) return new result.Err(state); if (index.shouldUseCacheIfStale(init?.cache) && time.Time.isAfterNow(state?.real?.current.expiration)) return new result.Err(state); if (!index.shouldUseNetwork(init?.cache)) throw new Error(`Could not fetch using the provided cache directive`); const aborter = new AbortController(); const signal = AbortSignal.any([aborter.signal, index$1.AbortSignals.getOrNever(init?.signal)]); return new result.Ok(await core.core.runOrReplace(cacheKey, aborter, () => helper.Scrollable.scrollOrThrow(cacheKey, signal, settings))); }, [cacheKey]); const state = stateRef.current; const aborter = aborterRef.current; const ready = state != null; const fetching = aborter != null; const optimistic = state?.isFake(); const current = state?.current; const data = state?.data; const error = state?.error; const real = state?.real; const fake = state?.fake; const peekOrNull = react.useCallback(() => { const pages = state?.real?.data?.get(); if (pages == null) return undefined; return settings.scroller(arrays.Arrays.last(pages)); }, [state?.real?.data, settings.scroller]); return { ...settings, cacheKey, current, data, error, real, fake, ready, optimistic, aborter, fetching, mutateOrThrow, fetchOrThrow, refetchOrThrow, scrollOrThrow, deleteOrThrow, peekOrNull }; } exports.useFetcherfulScrollableQuery = useFetcherfulScrollableQuery; exports.useFetcherlessScrollableQuery = useFetcherlessScrollableQuery; exports.useScrollableQuery = useScrollableQuery; exports.useSkeletonScrollableQuery = useSkeletonScrollableQuery; //# sourceMappingURL=scroll.cjs.map