@hyper-fetch/react
Version:
React hooks and utils for the hyper-fetch
161 lines (144 loc) • 4.95 kB
text/typescript
/* eslint-disable @typescript-eslint/naming-convention */
import {
CacheValueType,
NullableType,
RequestInstance,
ExtractResponseType,
ExtractErrorType,
ExtractAdapterResolvedType,
Dispatcher,
ExtractAdapterType,
ExtractAdapterExtraType,
ResponseDetailsType,
ExtractAdapterStatusType,
ResponseType,
} from "@hyper-fetch/core";
import { initialState, UseTrackedStateType } from "helpers";
export const getDetailsState = (
state?: UseTrackedStateType<RequestInstance>,
details?: Partial<ResponseDetailsType>,
): ResponseDetailsType => {
return {
retries: state?.retries || 0,
isCanceled: false,
isOffline: false,
addedTimestamp: +new Date(),
triggerTimestamp: +new Date(),
requestTimestamp: +new Date(),
responseTimestamp: +new Date(),
...details,
};
};
export const isStaleCacheData = (staleTime: number, staleTimestamp: NullableType<Date | number>) => {
if (!staleTimestamp) return true;
return +new Date() > +staleTimestamp + staleTime;
};
export const getValidCacheData = <T extends RequestInstance>(
request: T,
initialResponse: NullableType<Partial<ExtractAdapterResolvedType<T>>>,
cacheData: NullableType<CacheValueType<ExtractResponseType<T>, ExtractErrorType<T>, ExtractAdapterType<T>>>,
): CacheValueType<ExtractResponseType<T>, ExtractErrorType<T>, ExtractAdapterType<T>> | null => {
const isStale = isStaleCacheData(request.staleTime, cacheData?.responseTimestamp);
if (!isStale && cacheData) {
return cacheData;
}
if (initialResponse) {
return {
data: null,
error: null,
status: null,
success: true,
extra: null,
cached: !!request.cache,
...(initialResponse as Partial<ExtractAdapterResolvedType<T>>),
...getDetailsState(),
staleTime: 1000,
version: request.client.cache.version,
cacheKey: request.cacheKey,
cacheTime: request.cacheTime,
requestTimestamp: initialResponse?.requestTimestamp ?? +new Date(),
responseTimestamp: initialResponse?.responseTimestamp ?? +new Date(),
};
}
return null;
};
export const getTimestamp = (timestamp?: NullableType<number | Date>) => {
return timestamp ? new Date(timestamp) : null;
};
export const getIsInitiallyLoading = <T extends RequestInstance>({
queryKey,
dispatcher,
hasState,
revalidate,
disabled,
}: {
queryKey: string;
dispatcher: Dispatcher<ExtractAdapterType<T>>;
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<ExtractAdapterResolvedType<T>>>;
dispatcher: Dispatcher<ExtractAdapterType<T>>;
request: T;
disabled?: boolean; // useFetch only
revalidate?: boolean; // useFetch only
}): UseTrackedStateType<T> => {
const { client, cacheKey, unstable_responseMapper } = request;
const { cache } = client;
const cacheData = cache.get<ExtractResponseType<T>, ExtractErrorType<T>>(cacheKey);
const cacheState = getValidCacheData<T>(request, initialResponse, cacheData);
const initialLoading = getIsInitiallyLoading({
queryKey: request.queryKey,
dispatcher,
disabled,
revalidate,
hasState: !!cacheState,
});
if (cacheState) {
const mappedData = unstable_responseMapper
? unstable_responseMapper(cacheState as ResponseType<any, any, ExtractAdapterType<T>>)
: 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 as ExtractAdapterStatusType<ExtractAdapterType<T>>,
success: mappedData.success,
extra: (mappedData.extra || client.adapter.defaultExtra) as ExtractAdapterExtraType<ExtractAdapterType<T>>,
retries: cacheState.retries,
requestTimestamp: getTimestamp(cacheState.requestTimestamp),
responseTimestamp: getTimestamp(cacheState.responseTimestamp),
loading: initialLoading,
};
}
return {
data: initialState.data,
error: initialState.error,
status: initialState.status as ExtractAdapterStatusType<ExtractAdapterType<T>>,
success: initialState.success,
extra: request.client.adapter.defaultExtra as ExtractAdapterExtraType<ExtractAdapterType<T>>,
retries: initialState.retries,
requestTimestamp: getTimestamp(initialState.requestTimestamp),
responseTimestamp: getTimestamp(initialState.responseTimestamp),
loading: initialLoading,
};
};