UNPKG

@hyper-fetch/react

Version:

React hooks and utils for the hyper-fetch

157 lines (140 loc) 3.99 kB
import { CacheValueType, NullableType, RequestInstance, ExtractResponseType, ExtractErrorType, ExtractAdapterReturnType, Dispatcher, ExtractAdapterType, ExtractAdapterExtraType, ResponseDetailsType, } from "@hyper-fetch/core"; import { initialState, UseTrackedStateType } from "helpers"; export const getDetailsState = ( state?: UseTrackedStateType<RequestInstance>, details?: Partial<ResponseDetailsType>, ): ResponseDetailsType => { return { retries: state?.retries || 0, timestamp: +new Date(), isCanceled: false, isOffline: false, ...details, }; }; export const isStaleCacheData = (cacheTime: number, cacheTimestamp: NullableType<Date | number>) => { if (!cacheTimestamp) return true; return +new Date() > +cacheTimestamp + cacheTime; }; export const getValidCacheData = <T extends RequestInstance>( request: T, initialData: NullableType<Partial<ExtractAdapterReturnType<T>>>, cacheData: NullableType<CacheValueType<ExtractResponseType<T>, ExtractErrorType<T>>>, ): CacheValueType<ExtractResponseType<T>, ExtractErrorType<T>> | null => { const isStale = isStaleCacheData(request.cacheTime, cacheData?.timestamp); if (!isStale && cacheData) { return cacheData; } if (initialData) { return { data: null, error: null, status: null, success: null, extra: null, ...((initialData || {}) as Partial<ExtractAdapterReturnType<T>>), ...getDetailsState(), cacheTime: 1000, clearKey: request.client.cache.clearKey, garbageCollection: request.garbageCollection, }; } return null; }; export const getTimestamp = (timestamp?: NullableType<number | Date>) => { return timestamp ? new Date(timestamp) : null; }; export const getIsInitiallyLoading = ({ queryKey, dispatcher, hasState, revalidate, disabled, }: { queryKey: string; dispatcher: Dispatcher; hasState: boolean; revalidate?: boolean; disabled?: boolean; }) => { if (!revalidate && hasState) { return false; } const queue = dispatcher.getQueue(queryKey); const isInitiallyLoading = dispatcher.hasRunningRequests(queryKey) || (!queue.stopped && disabled === false); return isInitiallyLoading; }; export const getInitialState = <T extends RequestInstance>({ initialResponse, dispatcher, request, disabled, revalidate, }: { initialResponse: NullableType<Partial<ExtractAdapterReturnType<T>>>; dispatcher: Dispatcher; request: T; /** * useFetch only */ disabled?: boolean; /** * useFetch only */ revalidate?: boolean; }): UseTrackedStateType<T> => { const { client, cacheKey, responseMapper } = request; const { cache } = client; const cacheData = cache.get< ExtractResponseType<T>, ExtractErrorType<T>, ExtractAdapterExtraType<ExtractAdapterType<T>> >(cacheKey); const cacheState = getValidCacheData<T>(request, initialResponse, cacheData); const initialLoading = getIsInitiallyLoading({ queryKey: request.queueKey, dispatcher, disabled, revalidate, hasState: !!cacheState, }); if (cacheState) { const mappedData = responseMapper ? responseMapper(cacheState) : cacheState; if (mappedData instanceof Promise) { // For the async mapper we cannot return async values // So we have return the initial state instead return initialState; } return { data: mappedData.data, error: mappedData.error, status: mappedData.status, success: mappedData.success, extra: mappedData.extra, retries: cacheState.retries, timestamp: getTimestamp(cacheState.timestamp), loading: initialLoading, }; } return { data: initialState.data, error: initialState.error, status: initialState.status, success: initialState.success, extra: request.client.defaultExtra, retries: initialState.retries, timestamp: getTimestamp(initialState.timestamp), loading: initialLoading, }; };