UNPKG

@connected/react

Version:

The hassle free way to call your server-side code

76 lines 2.93 kB
import React, { useCallback, useReducer, startTransition } from 'react'; import md5 from 'md5'; import stringify from 'fast-json-stable-stringify'; import ConnectedContext from './connected-context.js'; import ErrorHandlerContext from './error-handler-context.js'; function cacheKeyFn(fn, params, meta) { const { meta: functionMeta, ...functionProperties } = fn; const combinedMeta = functionMeta ?? meta; if (!combinedMeta) { console.warn(`Function ${fn.name} has no associated meta. Make sure your transpiler is configured correctly.`); } const name = combinedMeta?.name ?? fn.name; return md5(stringify([name, functionProperties, ...params])); } function addMilliseconds(milliseconds, date) { const t = date ?? new Date(); t.setMilliseconds(t.getMilliseconds() + milliseconds); return t; } export default function useCallResolver() { const { cache, dataTtl, errorTtl } = React.useContext(ConnectedContext); const { onError: handleError } = React.useContext(ErrorHandlerContext); const [, forceReload] = useReducer((x) => x + 1, 0); const tryFetchData = useCallback((cacheKey, fn, args, stalledData) => { let result; try { result = fn(...args); } catch (error) { cache.set(cacheKey, { error, ttl: addMilliseconds(errorTtl) }); result = handleError(error, stalledData); } const entry = cache.get(cacheKey); if (result instanceof Promise) { result .then((data) => { cache.set(cacheKey, { data, ttl: addMilliseconds(dataTtl) }); }) .catch((error) => { cache.set(cacheKey, { error, data: stalledData, ttl: addMilliseconds(errorTtl), }); }); if (entry?.data !== undefined) { result.finally(() => { startTransition(() => { forceReload(); }); }); return entry.data; } throw result; } return result; }, [cache, dataTtl, errorTtl, handleError]); return useCallback((fn, args, meta) => { if (typeof fn !== 'function') { throw new TypeError(`${fn} is not a function`); } const cacheKey = cacheKeyFn(fn, args, meta); const entry = cache.get(cacheKey); if (entry) { if (entry.ttl < new Date()) { return tryFetchData(cacheKey, fn, args, entry.data); } if (entry.error) { return handleError(entry.error, entry.data); } return entry.data; } return tryFetchData(cacheKey, fn, args, undefined); }, [cache, handleError, tryFetchData]); } //# sourceMappingURL=use-call-resolver.js.map