Files
vf_react/src/hooks/usePagination.ts
zdl 3a02c13dfe fix: 修复评论在所有事件中串联显示的严重 Bug
问题描述:
- 在事件 A 下发表评论后,该评论会出现在事件 B、C 等所有事件下
- 切换事件时,评论列表没有重新加载,导致数据混乱

根本原因:
- usePagination Hook 的 useEffect 只依赖 autoLoad(常量)
- 当 eventId 变化时,loadCommentsFunction 被重新创建(包含新的 eventId)
- 但 useEffect 不会重新执行,导致旧数据(上一个事件的评论)持续显示

修复方案:
- 在 useEffect 依赖数组中添加 loadFunction
- 当 loadFunction 变化时(eventId 变化 → loadCommentsFunction 变化)
- useEffect 重新执行,加载新事件的评论数据

影响范围:
- EventCommentSection 组件(评论区)
- 所有使用 usePagination Hook 的组件都会受益于此修复
- 确保数据隔离性和正确性

🤖 Generated with Claude Code
2025-11-17 10:30:57 +08:00

138 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* usePagination - 通用分页 Hook
*
* 封装分页逻辑,支持初始加载、加载更多、重置等功能
*
* @example
* const {
* data: comments,
* loading,
* loadingMore,
* hasMore,
* totalCount,
* loadMore,
* setData,
* setTotalCount,
* } = usePagination<Comment>(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<T>(
loadFunction: LoadFunction<T>,
options: UsePaginationOptions = {}
): UsePaginationResult<T> {
const { pageSize = 10, autoLoad = true } = options;
// 状态管理
const [data, setData] = useState<T[]>([]);
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<T> = 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, loadFunction]);
return {
data,
loading,
loadingMore,
currentPage,
hasMore,
totalCount,
loadMore,
reset,
setData,
setTotalCount,
};
}