@router-cli/react-router
Version:
File based routing cli for react-router-dom. Types and components. see @router-cli/react-router-dev for the cli and plugins.
76 lines (64 loc) • 1.89 kB
text/typescript
type CachedItem = {
promise: Promise<unknown>
key: string
error?: any
response?: unknown
timeout?: ReturnType<typeof setTimeout>
remove: () => void
}
const isPromise = (promise: any): promise is Promise<unknown> =>
typeof promise === 'object' && typeof (promise as Promise<any>).then === 'function'
const _cache: Map<string, CachedItem> = new Map();
type PromiseOrFunc<TData> = (() => Promise<TData>) | Promise<TData>;
function query<TData>({ func, key, preload }: {
func: PromiseOrFunc<TData>,
key: string,
preload?: boolean
}) {
const cachedEntry = _cache.get(key);
if (cachedEntry) {
if ("error" in cachedEntry && cachedEntry.error) throw cachedEntry.error;
if ("response" in cachedEntry) return cachedEntry.response as TData;
if (!preload) throw cachedEntry.promise
}
const entry: CachedItem = {
key,
remove: () => _cache.delete(key),
promise:
(isPromise(func) ? func : func())
.then((response) => {
entry.response = response;
return response;
})
.catch((error) => (entry.error = error)),
}
_cache.set(key, entry);
if (!preload) throw entry.promise
return undefined as unknown as TData;
}
export const useSuspendedPromise = <TData>(
func: PromiseOrFunc<TData>,
key: string,
) => query({ func, key, preload: false });
export const suspendedPromiseCache = {
preload: <TData>(
func: PromiseOrFunc<TData>,
key: string,
) => {
query({ func, key, preload: true });
},
preloadValue: <TData>(
value: TData,
key: string,
) => {
const entry = {
key,
response: value,
remove: () => _cache.delete(key),
promise: Promise.resolve(value),
};
_cache.set(key, entry);
return entry;
},
clear: (key: string) => _cache.delete(key),
}