@hyper-fetch/react
Version:
React hooks and utils for the hyper-fetch
157 lines (140 loc) • 3.99 kB
text/typescript
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,
};
};