Files
vf_react/src/hooks/useFollowingEvents.js
zdl 06475f82a4 refactor(events): 关注事件数据源统一到 Redux
- useFollowingEvents: 改用 Redux selector 获取关注事件
- GlobalSidebarContext: 移除本地 followingEvents 状态,使用 Redux
- 侧边栏和导航栏共享同一数据源,保持状态同步

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-23 20:17:57 +08:00

103 lines
3.7 KiB
JavaScript
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.

// src/hooks/useFollowingEvents.js
// 关注事件管理自定义 Hook与 Redux 状态同步,支持多组件共用)
import { useState, useCallback, useEffect, useRef } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useToast } from '@chakra-ui/react';
import { logger } from '../utils/logger';
import {
loadFollowingEvents as loadFollowingEventsAction,
toggleFollowEvent
} from '../store/slices/stockSlice';
const EVENTS_PAGE_SIZE = 8;
/**
* 关注事件管理 Hook导航栏专用
* 提供关注事件加载、分页、取消关注等功能
* 监听 Redux 中的 followingEvents 变化,自动同步
*
* @returns {{
* followingEvents: Array,
* eventsLoading: boolean,
* eventsPage: number,
* setEventsPage: Function,
* EVENTS_PAGE_SIZE: number,
* loadFollowingEvents: Function,
* handleUnfollowEvent: Function
* }}
*/
export const useFollowingEvents = () => {
const toast = useToast();
const dispatch = useDispatch();
const [eventsPage, setEventsPage] = useState(1);
// 从 Redux 获取关注事件数据(与 GlobalSidebar 共用)
const followingEvents = useSelector(state => state.stock.followingEvents || []);
const eventsLoading = useSelector(state => state.stock.loading?.followingEvents || false);
// 从 Redux 获取关注事件列表长度(用于监听变化)
const reduxEventsLength = useSelector(state => state.stock.followingEvents?.length || 0);
// 用于跟踪上一次的事件长度
const prevEventsLengthRef = useRef(-1);
// 初始化时加载 Redux followingEvents确保 Redux 状态被初始化)
const hasInitializedRef = useRef(false);
useEffect(() => {
if (!hasInitializedRef.current) {
hasInitializedRef.current = true;
logger.debug('useFollowingEvents', '初始化 Redux followingEvents');
dispatch(loadFollowingEventsAction());
}
}, [dispatch]);
// 加载关注事件(通过 Redux
const loadFollowingEvents = useCallback(() => {
logger.debug('useFollowingEvents', '触发 loadFollowingEvents');
dispatch(loadFollowingEventsAction());
}, [dispatch]);
// 监听 Redux followingEvents 长度变化,自动更新分页
useEffect(() => {
const currentLength = reduxEventsLength;
const prevLength = prevEventsLengthRef.current;
// 当事件列表长度变化时,更新分页(确保不超出范围)
if (prevLength !== -1 && currentLength !== prevLength) {
const newMaxPage = Math.max(1, Math.ceil(currentLength / EVENTS_PAGE_SIZE));
setEventsPage(p => Math.min(p, newMaxPage));
}
prevEventsLengthRef.current = currentLength;
}, [reduxEventsLength]);
// 取消关注事件(通过 Redux
const handleUnfollowEvent = useCallback(async (eventId) => {
try {
// 通过 Redux action 取消关注(乐观更新)
await dispatch(toggleFollowEvent({
eventId,
isFollowing: true // 表示当前已关注,需要取消
})).unwrap();
toast({ title: '已取消关注该事件', status: 'info', duration: 1500 });
} catch (e) {
logger.error('useFollowingEvents', '取消关注事件失败', e);
toast({ title: e.message || '操作失败', status: 'error', duration: 2000 });
// 失败时重新加载列表
dispatch(loadFollowingEventsAction());
}
}, [dispatch, toast]);
return {
followingEvents,
eventsLoading,
eventsPage,
setEventsPage,
EVENTS_PAGE_SIZE,
loadFollowingEvents,
handleUnfollowEvent
};
};