// 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 }); }, []); // 兼容旧 API:setDetailDrawerVisible const setDetailDrawerVisible = useCallback((visible) => { if (visible) { // 如果要显示,但没有内容,不做任何事 } else { dispatch({ type: ActionTypes.CLOSE_CONTENT_DETAIL }); } }, []); // 兼容旧 API:setSelectedContent 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;