import { useCallback, useEffect, useRef, useState } from "react"; interface AsyncState { data: T | null; loading: boolean; error: string | null; reload: () => void; } /** Runs an async factory on mount (and on reload), tracking loading/error so * every API-backed surface has a complete state. Best-effort, no `any`. */ export function useAsyncData(fn: () => Promise, deps: readonly unknown[] = []): AsyncState { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const fnRef = useRef(fn); fnRef.current = fn; const depsKey = deps.join("\u0000"); const run = useCallback(() => { let cancelled = false; setLoading(true); setError(null); fnRef .current() .then((res) => { if (!cancelled) setData(res); }) .catch((err: unknown) => { if (!cancelled) setError(err instanceof Error ? err.message : "加载失败"); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [depsKey]); useEffect(() => { const cancel = run(); return cancel; }, [run]); return { data, loading, error, reload: run }; }