import { useEffect, useState, useCallback } from 'react'; // 모듈 레벨 캐시: key -> 완료된 데이터 const cache = new Map<string, unknown>(); // 모듈 레벨 in-flight 요청: key -> 진행 중 Promise (디덥용) const inflight = new Map<string, Promise<unknown>>(); export function useResource<T>(key: string, fetcher: () => Promise<T>) { const [data, setData] = useState<T | undefined>(() => cache.get(key) as T | undefined); const [error, setError] = useState<Error | null>(null); const [isLoading, setIsLoading] = useState<boolean>(() => !cache.has(key)); const [version, setVersion] = useState(0); useEffect(() => { // TODO: // 1) cache.has(key)면 캐시값으로 data 채우고 isLoading=false로 빠르게 반환 // 2) inflight.has(key)면 그 promise를 await (디덥) // 3) 그렇지 않으면 fetcher()를 호출해 inflight에 저장, 완료되면 cache에 저장 + inflight 제거 // 4) 결과를 data에 반영, 실패 시 error 반영 let cancelled = false; // TODO: 위 로직 구현 return () => { cancelled = true; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [key, version]); const refetch = useCallback(() => { cache.delete(key); inflight.delete(key); setVersion(v => v + 1); }, [key]); return { data, error, isLoading, refetch }; } // 테스트가 캐시를 초기화할 수 있도록 export export function __resetResourceCache() { cache.clear(); inflight.clear(); }
Tests