feat: 实现 Socket 触发的智能列表自动刷新功能(带防抖)
核心改动: - 扩展 NotificationContext,添加事件更新回调注册机制 - VirtualizedFourRowGrid 添加 forwardRef 暴露 getScrollPosition 方法 - DynamicNewsCard 实现智能刷新逻辑(根据模式和滚动位置判断是否刷新) - Community 页面注册 Socket 回调自动触发刷新 - 创建 TypeScript 通用防抖工具函数(debounce.ts) - 集成防抖机制(2秒延迟),避免短时间内频繁请求 智能刷新策略: - 纵向模式 + 第1页:自动刷新列表 - 纵向模式 + 其他页:不刷新(避免打断用户) - 平铺模式 + 滚动在顶部:自动刷新列表 - 平铺模式 + 滚动不在顶部:仅显示 Toast 提示 防抖效果: - 短时间内收到多个新事件,只执行最后一次刷新 - 减少服务器压力,提升用户体验 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,7 @@ import VerticalModeLayout from './VerticalModeLayout';
|
||||
* @param {boolean} hasMore - 是否还有更多数据
|
||||
* @param {Object} eventFollowStatus - 事件关注状态 { [eventId]: { isFollowing, followerCount } }
|
||||
* @param {Function} onToggleFollow - 关注按钮回调
|
||||
* @param {React.Ref} virtualizedGridRef - VirtualizedFourRowGrid 的 ref(用于获取滚动位置)
|
||||
*/
|
||||
const EventScrollList = ({
|
||||
events,
|
||||
@@ -46,7 +47,8 @@ const EventScrollList = ({
|
||||
mode = 'vertical',
|
||||
hasMore = true,
|
||||
eventFollowStatus = {},
|
||||
onToggleFollow
|
||||
onToggleFollow,
|
||||
virtualizedGridRef
|
||||
}) => {
|
||||
const scrollContainerRef = useRef(null);
|
||||
|
||||
@@ -111,6 +113,7 @@ const EventScrollList = ({
|
||||
>
|
||||
{/* 平铺网格模式 - 使用虚拟滚动 + 双向无限滚动 */}
|
||||
<VirtualizedFourRowGrid
|
||||
ref={virtualizedGridRef} // ⚡ 传递 ref(用于获取滚动位置)
|
||||
display={mode === 'four-row' ? 'block' : 'none'}
|
||||
columnsPerRow={4} // 每行显示4列
|
||||
events={displayEvents || events} // 使用累积列表(如果有)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// src/views/Community/components/DynamicNewsCard/VirtualizedFourRowGrid.js
|
||||
// 虚拟化网格组件(支持多列布局 + 纵向滚动 + 无限滚动)
|
||||
|
||||
import React, { useRef, useMemo, useEffect } from 'react';
|
||||
import React, { useRef, useMemo, useEffect, forwardRef, useImperativeHandle } from 'react';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import { Box, Grid, Spinner, Text, VStack, Center, HStack, IconButton, useBreakpointValue } from '@chakra-ui/react';
|
||||
import { RepeatIcon } from '@chakra-ui/icons';
|
||||
@@ -25,7 +25,7 @@ import DynamicNewsEventCard from '../EventCard/DynamicNewsEventCard';
|
||||
* @param {boolean} props.hasMore - 是否还有更多数据
|
||||
* @param {boolean} props.loading - 加载状态
|
||||
*/
|
||||
const VirtualizedFourRowGrid = ({
|
||||
const VirtualizedFourRowGrid = forwardRef(({
|
||||
display = 'block',
|
||||
events,
|
||||
columnsPerRow = 4,
|
||||
@@ -42,7 +42,7 @@ const VirtualizedFourRowGrid = ({
|
||||
loading,
|
||||
error, // 新增:错误状态
|
||||
onRetry, // 新增:重试回调
|
||||
}) => {
|
||||
}, ref) => {
|
||||
const parentRef = useRef(null);
|
||||
const isLoadingMore = useRef(false); // 防止重复加载
|
||||
const lastRefreshTime = useRef(0); // 记录上次刷新时间(用于30秒防抖)
|
||||
@@ -81,6 +81,31 @@ const VirtualizedFourRowGrid = ({
|
||||
overscan: 2, // 预加载2行(上下各1行)
|
||||
});
|
||||
|
||||
/**
|
||||
* ⚡ 暴露方法给父组件(用于 Socket 刷新判断)
|
||||
*/
|
||||
useImperativeHandle(ref, () => ({
|
||||
/**
|
||||
* 获取当前滚动位置信息
|
||||
* @returns {Object|null} 滚动位置信息
|
||||
*/
|
||||
getScrollPosition: () => {
|
||||
const scrollElement = parentRef.current;
|
||||
if (!scrollElement) return null;
|
||||
|
||||
const { scrollTop, scrollHeight, clientHeight } = scrollElement;
|
||||
const isNearTop = scrollTop < clientHeight * 0.1; // 顶部 10% 区域
|
||||
|
||||
return {
|
||||
scrollTop,
|
||||
scrollHeight,
|
||||
clientHeight,
|
||||
isNearTop,
|
||||
scrollPercentage: ((scrollTop + clientHeight) / scrollHeight) * 100,
|
||||
};
|
||||
},
|
||||
}), []);
|
||||
|
||||
/**
|
||||
* 【核心逻辑1】无限滚动 + 顶部刷新 - 监听滚动事件,根据滚动位置自动加载数据或刷新
|
||||
*
|
||||
@@ -360,6 +385,8 @@ const VirtualizedFourRowGrid = ({
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
VirtualizedFourRowGrid.displayName = 'VirtualizedFourRowGrid';
|
||||
|
||||
export default VirtualizedFourRowGrid;
|
||||
|
||||
@@ -38,3 +38,21 @@ export const TOAST_CONFIG = {
|
||||
DURATION_ERROR: 3000, // 错误提示持续时间(毫秒)
|
||||
DURATION_WARNING: 2000, // 警告提示持续时间(毫秒)
|
||||
};
|
||||
|
||||
// ========== Socket 刷新防抖配置 ==========
|
||||
/**
|
||||
* Socket 新事件刷新防抖延迟(毫秒)
|
||||
*
|
||||
* 作用:避免短时间内收到多个新事件时频繁刷新列表
|
||||
*
|
||||
* 场景示例:
|
||||
* - 第 1 秒:收到新事件 → 延迟 2 秒刷新
|
||||
* - 第 2 秒:收到新事件 → 取消上次,重新延迟 2 秒
|
||||
* - 第 3 秒:收到新事件 → 取消上次,重新延迟 2 秒
|
||||
* - 第 5 秒:触发刷新 → 只发送 1 次 API 请求
|
||||
*
|
||||
* 推荐值:2000ms (2 秒)
|
||||
* - 太短(如 500ms)→ 仍可能触发多次刷新
|
||||
* - 太长(如 5000ms)→ 用户感知延迟过高
|
||||
*/
|
||||
export const REFRESH_DEBOUNCE_DELAY = 2000;
|
||||
|
||||
Reference in New Issue
Block a user