/** * usePagination - 通用分页 Hook * * 封装分页逻辑,支持初始加载、加载更多、重置等功能 * * @example * const { * data: comments, * loading, * loadingMore, * hasMore, * totalCount, * loadMore, * setData, * setTotalCount, * } = usePagination(loadCommentsFunction, { pageSize: 5 }); */ import { useState, useCallback, useEffect } from 'react'; import type { LoadFunction, PaginationLoadResult, UsePaginationOptions, UsePaginationResult, } from '@/types/pagination'; /** * usePagination Hook * @template T 数据项类型 * @param loadFunction 加载函数,接收 (page, append) 参数 * @param options 配置选项 * @returns 分页状态和操作方法 */ export function usePagination( loadFunction: LoadFunction, options: UsePaginationOptions = {} ): UsePaginationResult { const { pageSize = 10, autoLoad = true } = options; // 状态管理 const [data, setData] = useState([]); const [loading, setLoading] = useState(false); const [loadingMore, setLoadingMore] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [totalCount, setTotalCount] = useState(0); const [hasMore, setHasMore] = useState(false); /** * 加载数据 * @param page 页码 * @param append 是否追加(true: 追加,false: 替换) */ const loadData = useCallback( async (page: number, append: boolean = false) => { // 设置加载状态 if (append) { setLoadingMore(true); } else { setLoading(true); } try { const result: PaginationLoadResult = await loadFunction(page, append); // 更新数据 if (append) { setData((prevData) => [...prevData, ...(result.data || [])]); } else { setData(result.data || []); } // 更新分页信息 const total = result.pagination?.total || result.data?.length || 0; setTotalCount(total); // 计算是否还有更多数据 const currentTotal = append ? data.length + (result.data?.length || 0) : result.data?.length || 0; setHasMore(currentTotal < total); } catch (error) { console.error('[usePagination] 加载数据失败:', error); throw error; } finally { if (append) { setLoadingMore(false); } else { setLoading(false); } } }, [loadFunction, data.length] ); /** * 加载更多数据 */ const loadMore = useCallback(async () => { if (loadingMore || !hasMore) return; const nextPage = currentPage + 1; await loadData(nextPage, true); setCurrentPage(nextPage); }, [currentPage, loadData, loadingMore, hasMore]); /** * 重置到第一页 */ const reset = useCallback(() => { setCurrentPage(1); setData([]); setTotalCount(0); setHasMore(false); loadData(1, false); }, [loadData]); // 自动加载第一页 useEffect(() => { if (autoLoad) { loadData(1, false); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [autoLoad]); return { data, loading, loadingMore, currentPage, hasMore, totalCount, loadMore, reset, setData, setTotalCount, }; }