use-async-resource
Version:
A custom React hook for simple data fetching with React Suspense
76 lines (64 loc) • 2.21 kB
text/typescript
import { ApiFn, DataOrModifiedFn, ModifierFn } from './types';
import { resourceCache } from './cache';
/**
* Wrapper for an apiFunction without params.
* It only takes the api function as an argument.
* It returns a data reader with an optional modifier function.
*
* @param apiFn A typical api function that doesn't take any parameters.
*/
export function initializeDataReader<ResponseType>(
apiFn: ApiFn<ResponseType>,
): DataOrModifiedFn<ResponseType>;
/**
* Wrapper for an apiFunction with params.
* It takes the api function and all its expected arguments.
* Also returns a data reader with an optional modifier function.
*
* @param apiFn A typical api function with parameters.
* @param parameters An arbitrary number of parameters.
*/
export function initializeDataReader<ResponseType, ArgTypes extends unknown[]>(
apiFn: ApiFn<ResponseType, ArgTypes>,
...parameters: ArgTypes
): DataOrModifiedFn<ResponseType>;
// implementation that covers the above overloads
export function initializeDataReader<ResponseType, ArgTypes extends unknown[] = []>(
apiFn: ApiFn<ResponseType, ArgTypes>,
...parameters: ArgTypes
) {
type AsyncStatus = 'init' | 'done' | 'error';
const apiFnCache = resourceCache(apiFn);
const cachedResource = apiFnCache.get(...parameters);
if (cachedResource) {
return cachedResource;
}
let data: ResponseType;
let status: AsyncStatus = 'init';
let error: unknown;
const fetchingPromise = apiFn(...parameters)
.then((result) => {
data = result;
status = 'done';
return result;
})
.catch((err) => {
error = err;
status = 'error';
});
// the return type successfully satisfies DataOrModifiedFn<ResponseType>
function dataReaderFn(): ResponseType;
function dataReaderFn<M>(modifier: ModifierFn<ResponseType, M>): M;
function dataReaderFn<M>(modifier?: ModifierFn<ResponseType, M>) {
if (status === 'init') {
throw fetchingPromise;
} else if (status === 'error') {
throw error;
}
return typeof modifier === 'function'
? (modifier(data) as M)
: (data as ResponseType);
}
apiFnCache.set(dataReaderFn, ...parameters);
return dataReaderFn;
}