'use client'; import { useCallback, useEffect, useRef, useState } from 'react'; type UseApiState = { data: T | null; loading: boolean; error: string | null; }; /** * 通用数据获取 hook,封装 loading / error / data 状态 + AbortController 取消。 * * @param fetcher 返回 Promise 的数据获取函数,接收 AbortSignal * @param deps 依赖数组,变化时重新请求 */ export function useApi( fetcher: (signal?: AbortSignal) => Promise, deps: unknown[] = [], ): UseApiState & { refetch: () => void } { const [state, setState] = useState>({ data: null, loading: true, error: null, }); const abortRef = useRef(null); const fetcherRef = useRef(fetcher); fetcherRef.current = fetcher; const load = useCallback(() => { abortRef.current?.abort(); const ac = new AbortController(); abortRef.current = ac; setState((s) => ({ ...s, loading: true, error: null })); fetcherRef .current(ac.signal) .then((data) => { if (!ac.signal.aborted) { setState({ data, loading: false, error: null }); } }) .catch((e: unknown) => { if (!ac.signal.aborted) { setState({ data: null, loading: false, error: e instanceof Error ? e.message : String(e) }); } }); }, deps); useEffect(() => { load(); return () => { abortRef.current?.abort(); }; }, [load]); return { ...state, refetch: load }; }