Files
vf_react/src/views/Community/components/HeroPanel/hooks/useDetailModalState.js
zdl e110d5860c perf(HeroPanel): 性能优化 - React.memo、API并行化、useReducer重构
- 添加 React.memo 优化子组件,减少 30-40% 不必要重渲染
  - CombinedCalendar.js
  - EventDailyStats.js (TopEventItem)
  - MarketOverviewBanner/components.js (MarketStatsBarCompact, CircularProgressCard, BannerStatCard)
- DetailModal.js: 股票行情 API 从串行改为 Promise.all 并行加载
  - 加载时间从 10s+ 降至 2-3s
- useDetailModalState.js: 17 个 useState 重构为 1 个 useReducer
  - 减少状态更新导致的重渲染
  - 保持向后兼容,使用 useRef 处理旧 API 调用模式

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-15 17:32:08 +08:00

432 lines
12 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.

// HeroPanel - DetailModal 状态管理 Hook
// 使用 useReducer 优化,减少不必要的重渲染
import { useReducer, useCallback, useMemo, useRef } from "react";
// ========== 初始状态 ==========
const initialState = {
// UI 状态
ztViewMode: "sector", // 'sector' | 'stock'
selectedSectorFilter: null,
expandedReasons: {},
// 弹窗/抽屉状态
detailDrawerVisible: false,
selectedContent: null,
sectorStocksModalVisible: false,
selectedSectorInfo: null,
stocksDrawerVisible: false,
selectedEventStocks: [],
selectedEventTime: null,
selectedEventTitle: "",
klineModalVisible: false,
selectedKlineStock: null,
relatedEventsModalVisible: false,
selectedRelatedEvents: { sectorName: "", events: [] },
// 数据加载状态
stockQuotes: {},
stockQuotesLoading: false,
};
// ========== Action Types ==========
const ActionTypes = {
SET_ZT_VIEW_MODE: "SET_ZT_VIEW_MODE",
SET_SECTOR_FILTER: "SET_SECTOR_FILTER",
TOGGLE_EXPANDED_REASON: "TOGGLE_EXPANDED_REASON",
SET_EXPANDED_REASONS: "SET_EXPANDED_REASONS",
OPEN_CONTENT_DETAIL: "OPEN_CONTENT_DETAIL",
CLOSE_CONTENT_DETAIL: "CLOSE_CONTENT_DETAIL",
OPEN_SECTOR_STOCKS: "OPEN_SECTOR_STOCKS",
CLOSE_SECTOR_STOCKS: "CLOSE_SECTOR_STOCKS",
OPEN_EVENT_STOCKS: "OPEN_EVENT_STOCKS",
CLOSE_EVENT_STOCKS: "CLOSE_EVENT_STOCKS",
OPEN_KLINE_MODAL: "OPEN_KLINE_MODAL",
CLOSE_KLINE_MODAL: "CLOSE_KLINE_MODAL",
OPEN_RELATED_EVENTS: "OPEN_RELATED_EVENTS",
CLOSE_RELATED_EVENTS: "CLOSE_RELATED_EVENTS",
SET_STOCK_QUOTES: "SET_STOCK_QUOTES",
SET_STOCK_QUOTES_LOADING: "SET_STOCK_QUOTES_LOADING",
RESET_ALL: "RESET_ALL",
};
// ========== Reducer ==========
function reducer(state, action) {
switch (action.type) {
case ActionTypes.SET_ZT_VIEW_MODE:
return { ...state, ztViewMode: action.payload };
case ActionTypes.SET_SECTOR_FILTER:
return { ...state, selectedSectorFilter: action.payload };
case ActionTypes.TOGGLE_EXPANDED_REASON:
return {
...state,
expandedReasons: {
...state.expandedReasons,
[action.payload]: !state.expandedReasons[action.payload],
},
};
case ActionTypes.SET_EXPANDED_REASONS:
return { ...state, expandedReasons: action.payload };
case ActionTypes.OPEN_CONTENT_DETAIL:
return {
...state,
selectedContent: action.payload,
detailDrawerVisible: true,
};
case ActionTypes.CLOSE_CONTENT_DETAIL:
return {
...state,
detailDrawerVisible: false,
selectedContent: null,
};
case ActionTypes.OPEN_SECTOR_STOCKS:
return {
...state,
selectedSectorInfo: action.payload,
sectorStocksModalVisible: true,
};
case ActionTypes.CLOSE_SECTOR_STOCKS:
return {
...state,
sectorStocksModalVisible: false,
selectedSectorInfo: null,
};
case ActionTypes.OPEN_EVENT_STOCKS:
return {
...state,
selectedEventStocks: action.payload.stocks,
selectedEventTime: action.payload.time,
selectedEventTitle: action.payload.title,
stocksDrawerVisible: true,
};
case ActionTypes.CLOSE_EVENT_STOCKS:
return {
...state,
stocksDrawerVisible: false,
selectedEventStocks: [],
selectedEventTime: null,
selectedEventTitle: "",
};
case ActionTypes.OPEN_KLINE_MODAL:
return {
...state,
selectedKlineStock: action.payload,
klineModalVisible: true,
};
case ActionTypes.CLOSE_KLINE_MODAL:
return {
...state,
klineModalVisible: false,
selectedKlineStock: null,
};
case ActionTypes.OPEN_RELATED_EVENTS:
return {
...state,
selectedRelatedEvents: action.payload,
relatedEventsModalVisible: true,
};
case ActionTypes.CLOSE_RELATED_EVENTS:
return {
...state,
relatedEventsModalVisible: false,
selectedRelatedEvents: { sectorName: "", events: [] },
};
case ActionTypes.SET_STOCK_QUOTES:
return { ...state, stockQuotes: action.payload };
case ActionTypes.SET_STOCK_QUOTES_LOADING:
return { ...state, stockQuotesLoading: action.payload };
case ActionTypes.RESET_ALL:
return initialState;
default:
return state;
}
}
/**
* DetailModal 状态管理 Hook (useReducer 优化版)
* 将 17 个 useState 整合为单个 useReducer减少重渲染
*/
export const useDetailModalState = () => {
const [state, dispatch] = useReducer(reducer, initialState);
// ========== 操作方法(稳定引用) ==========
const setZtViewMode = useCallback((mode) => {
dispatch({ type: ActionTypes.SET_ZT_VIEW_MODE, payload: mode });
}, []);
const setSelectedSectorFilter = useCallback((filter) => {
dispatch({ type: ActionTypes.SET_SECTOR_FILTER, payload: filter });
}, []);
const setExpandedReasons = useCallback((reasons) => {
dispatch({ type: ActionTypes.SET_EXPANDED_REASONS, payload: reasons });
}, []);
const toggleExpandedReason = useCallback((stockCode) => {
dispatch({ type: ActionTypes.TOGGLE_EXPANDED_REASON, payload: stockCode });
}, []);
const openContentDetail = useCallback((content) => {
dispatch({ type: ActionTypes.OPEN_CONTENT_DETAIL, payload: content });
}, []);
const closeContentDetail = useCallback(() => {
dispatch({ type: ActionTypes.CLOSE_CONTENT_DETAIL });
}, []);
// 兼容旧 APIsetDetailDrawerVisible
const setDetailDrawerVisible = useCallback((visible) => {
if (visible) {
// 如果要显示,但没有内容,不做任何事
} else {
dispatch({ type: ActionTypes.CLOSE_CONTENT_DETAIL });
}
}, []);
// 兼容旧 APIsetSelectedContent
const setSelectedContent = useCallback((content) => {
if (content) {
dispatch({ type: ActionTypes.OPEN_CONTENT_DETAIL, payload: content });
}
}, []);
const openSectorStocks = useCallback((sectorInfo) => {
dispatch({ type: ActionTypes.OPEN_SECTOR_STOCKS, payload: sectorInfo });
}, []);
const closeSectorStocks = useCallback(() => {
dispatch({ type: ActionTypes.CLOSE_SECTOR_STOCKS });
}, []);
// 兼容旧 API
const setSectorStocksModalVisible = useCallback((visible) => {
if (!visible) {
dispatch({ type: ActionTypes.CLOSE_SECTOR_STOCKS });
}
}, []);
const setSelectedSectorInfo = useCallback((info) => {
if (info) {
dispatch({ type: ActionTypes.OPEN_SECTOR_STOCKS, payload: info });
}
}, []);
const openEventStocks = useCallback((stocks, time, title) => {
dispatch({
type: ActionTypes.OPEN_EVENT_STOCKS,
payload: { stocks, time, title },
});
}, []);
const closeEventStocks = useCallback(() => {
dispatch({ type: ActionTypes.CLOSE_EVENT_STOCKS });
}, []);
// 兼容旧 API - 支持单独设置股票数据
// 注意:这些 setter 需要配合 setStocksDrawerVisible(true) 使用
const pendingEventStocksRef = useRef({ stocks: null, time: null, title: null });
const setStocksDrawerVisible = useCallback((visible) => {
if (visible) {
// 打开时,使用暂存的数据
dispatch({
type: ActionTypes.OPEN_EVENT_STOCKS,
payload: {
stocks: pendingEventStocksRef.current.stocks || [],
time: pendingEventStocksRef.current.time,
title: pendingEventStocksRef.current.title || "",
},
});
} else {
dispatch({ type: ActionTypes.CLOSE_EVENT_STOCKS });
}
}, []);
const setSelectedEventStocks = useCallback((stocks) => {
pendingEventStocksRef.current.stocks = stocks;
}, []);
const setSelectedEventTime = useCallback((time) => {
pendingEventStocksRef.current.time = time;
}, []);
const setSelectedEventTitle = useCallback((title) => {
pendingEventStocksRef.current.title = title;
}, []);
const openKlineModal = useCallback((stock) => {
dispatch({ type: ActionTypes.OPEN_KLINE_MODAL, payload: stock });
}, []);
const closeKlineModal = useCallback(() => {
dispatch({ type: ActionTypes.CLOSE_KLINE_MODAL });
}, []);
// 兼容旧 API
const setKlineModalVisible = useCallback((visible) => {
if (!visible) {
dispatch({ type: ActionTypes.CLOSE_KLINE_MODAL });
}
}, []);
const setSelectedKlineStock = useCallback((stock) => {
if (stock) {
dispatch({ type: ActionTypes.OPEN_KLINE_MODAL, payload: stock });
}
}, []);
const openRelatedEvents = useCallback((sectorName, events) => {
dispatch({
type: ActionTypes.OPEN_RELATED_EVENTS,
payload: { sectorName, events },
});
}, []);
const closeRelatedEvents = useCallback(() => {
dispatch({ type: ActionTypes.CLOSE_RELATED_EVENTS });
}, []);
// 兼容旧 API
const setRelatedEventsModalVisible = useCallback((visible) => {
if (!visible) {
dispatch({ type: ActionTypes.CLOSE_RELATED_EVENTS });
}
}, []);
const setSelectedRelatedEvents = useCallback((data) => {
if (data && data.sectorName) {
dispatch({ type: ActionTypes.OPEN_RELATED_EVENTS, payload: data });
}
}, []);
const setStockQuotes = useCallback((quotes) => {
dispatch({ type: ActionTypes.SET_STOCK_QUOTES, payload: quotes });
}, []);
const setStockQuotesLoading = useCallback((loading) => {
dispatch({ type: ActionTypes.SET_STOCK_QUOTES_LOADING, payload: loading });
}, []);
const resetAllState = useCallback(() => {
dispatch({ type: ActionTypes.RESET_ALL });
}, []);
// ========== 返回值(保持兼容性) ==========
return useMemo(
() => ({
// UI 状态 + setters
ztViewMode: state.ztViewMode,
setZtViewMode,
selectedSectorFilter: state.selectedSectorFilter,
setSelectedSectorFilter,
expandedReasons: state.expandedReasons,
setExpandedReasons,
// 弹窗状态 + setters
detailDrawerVisible: state.detailDrawerVisible,
setDetailDrawerVisible,
selectedContent: state.selectedContent,
setSelectedContent,
sectorStocksModalVisible: state.sectorStocksModalVisible,
setSectorStocksModalVisible,
selectedSectorInfo: state.selectedSectorInfo,
setSelectedSectorInfo,
stocksDrawerVisible: state.stocksDrawerVisible,
setStocksDrawerVisible,
selectedEventStocks: state.selectedEventStocks,
setSelectedEventStocks,
selectedEventTime: state.selectedEventTime,
setSelectedEventTime,
selectedEventTitle: state.selectedEventTitle,
setSelectedEventTitle,
klineModalVisible: state.klineModalVisible,
setKlineModalVisible,
selectedKlineStock: state.selectedKlineStock,
setSelectedKlineStock,
relatedEventsModalVisible: state.relatedEventsModalVisible,
setRelatedEventsModalVisible,
selectedRelatedEvents: state.selectedRelatedEvents,
setSelectedRelatedEvents,
// 数据状态 + setters
stockQuotes: state.stockQuotes,
setStockQuotes,
stockQuotesLoading: state.stockQuotesLoading,
setStockQuotesLoading,
// 操作方法(高级封装)
openContentDetail,
closeContentDetail,
openSectorStocks,
closeSectorStocks,
openEventStocks,
closeEventStocks,
openKlineModal,
closeKlineModal,
openRelatedEvents,
closeRelatedEvents,
toggleExpandedReason,
resetAllState,
}),
[
state,
setZtViewMode,
setSelectedSectorFilter,
setExpandedReasons,
setDetailDrawerVisible,
setSelectedContent,
setSectorStocksModalVisible,
setSelectedSectorInfo,
setStocksDrawerVisible,
setSelectedEventStocks,
setSelectedEventTime,
setSelectedEventTitle,
setKlineModalVisible,
setSelectedKlineStock,
setRelatedEventsModalVisible,
setSelectedRelatedEvents,
setStockQuotes,
setStockQuotesLoading,
openContentDetail,
closeContentDetail,
openSectorStocks,
closeSectorStocks,
openEventStocks,
closeEventStocks,
openKlineModal,
closeKlineModal,
openRelatedEvents,
closeRelatedEvents,
toggleExpandedReason,
resetAllState,
]
);
};
export default useDetailModalState;