/** * useHomeFilterFields Hook * * 首页筛选字段数据获取Hook,优化了以下功能: * - 使用SWR进行数据缓存和自动重新验证 * - 100ms内UI更新(无闪烁) * - 使用防抖技术避免重复请求 * - 支持手动刷新 * - 错误处理和重试 * * @created 2024-12-13 */ import { useCallback, useMemo } from 'react' import useSWR from 'swr' export interface FilterOption { label: string value: string count?: number } export interface FilterField { key: string label: string type?: string options: FilterOption[] } interface HomeFilterFieldsResponse { fields: FilterField[] } const CACHE_KEY = '/api/home-filter-fields' // SWR fetcher const fetcher = async (url: string): Promise => { const response = await fetch(url, { cache: 'no-store' }) if (!response.ok) { throw new Error('Failed to fetch filter fields') } return response.json() } interface UseHomeFilterFieldsOptions { /** 是否启用自动刷新 */ refreshInterval?: number /** 是否在窗口聚焦时重新验证 */ revalidateOnFocus?: boolean /** 是否在重新连接时重新验证 */ revalidateOnReconnect?: boolean } interface UseHomeFilterFieldsReturn { /** 筛选字段列表 */ fields: FilterField[] /** 字段映射(按key索引) */ fieldsMap: Record /** 是否正在加载 */ isLoading: boolean /** 是否正在验证(后台刷新) */ isValidating: boolean /** 错误信息 */ error: Error | undefined /** 手动刷新 */ refresh: () => Promise /** 数据是否已准备好 */ isReady: boolean } /** * 首页筛选字段数据获取Hook */ export function useHomeFilterFields( options: UseHomeFilterFieldsOptions = {} ): UseHomeFilterFieldsReturn { const { refreshInterval = 0, revalidateOnFocus = false, revalidateOnReconnect = true, } = options const { data, error, isLoading, isValidating, mutate } = useSWR( CACHE_KEY, fetcher, { refreshInterval, revalidateOnFocus, revalidateOnReconnect, // 保持之前的数据,避免闪烁 keepPreviousData: true, // 错误重试 errorRetryCount: 3, errorRetryInterval: 1000, // 去重请求 dedupingInterval: 2000, } ) const fields = useMemo(() => { if (!data?.fields) return [] // 规范化选项数据 return data.fields.map(field => ({ ...field, options: Array.isArray(field.options) ? field.options.map(opt => { if (typeof opt === 'string') { return { label: opt, value: opt } } return { label: opt.label || opt.value, value: opt.value, count: typeof opt.count === 'number' ? opt.count : undefined, } }).filter(opt => opt.value) : [], })) }, [data]) const fieldsMap = useMemo(() => { const map: Record = {} fields.forEach(field => { map[field.key] = field }) return map }, [fields]) const refresh = useCallback(async () => { await mutate() }, [mutate]) return { fields, fieldsMap, isLoading, isValidating, error, refresh, isReady: !isLoading && !error, } } /** * 获取特定字段的选项 */ export function useFilterFieldOptions(fieldKey: string): { options: FilterOption[] label: string isLoading: boolean } { const { fieldsMap, isLoading } = useHomeFilterFields() const field = fieldsMap[fieldKey] return { options: field?.options ?? [], label: field?.label ?? fieldKey, isLoading, } } export default useHomeFilterFields