import { useEffect, useState } from 'react'; interface UsePromiseOptions { cacheKey?: string; promiseFn?: () => Promise; } const PromiseCache = new Map(); type PromiseState = | { status: 'idle'; } | { status: 'pending'; } | { status: 'resolved'; data: T; } | { status: 'rejected'; error: Error; }; /** Simple implementation of a React hook for handling promises with caching. */ export function usePromise(options: UsePromiseOptions): { state: PromiseState; refetch: () => void; } { const [state, setState] = useState>({ status: 'idle' }); const [refetchKey, setRefetchKey] = useState(0); const hasCacheHit = PromiseCache.get(options.cacheKey || ''); useEffect(() => { if (!options.promiseFn || hasCacheHit) { return; } let safeSetState = setState; safeSetState({ status: 'pending' }); options .promiseFn() .then((data) => { safeSetState({ status: 'resolved', data }); if (options.cacheKey) { PromiseCache.set(options.cacheKey, { status: 'resolved', data }); } }) .catch((error) => { safeSetState({ status: 'rejected', error }); }); return () => { safeSetState = () => {}; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [options.cacheKey, hasCacheHit, refetchKey]); const characterState = hasCacheHit ? PromiseCache.get(options.cacheKey || '') : state; return { state: characterState, refetch: () => setRefetchKey(refetchKey + 1), }; }