Files
vf_react/src/views/Company/components/DeepAnalysis/hooks/useDeepAnalysisData.ts
zdl b7ad35ba12 fix(DeepAnalysis): 修复行业排名数据未加载导致无法点击弹窗问题
- TAB_API_MAP 改为数组形式,支持一个 Tab 加载多个 API
- strategy Tab 现在同时加载 comprehensive 和 industryRank 数据
- loadTabData 更新为遍历加载所有映射的 API
- currentLoading 计算改为检查任一相关 API 的 loading 状态
- 初始加载逻辑更新为加载 strategy Tab 的所有数据

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-26 15:09:42 +08:00

158 lines
3.9 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.

/**
* useDeepAnalysisData Hook
*
* 管理深度分析模块的数据获取逻辑:
* - 按 Tab 懒加载数据
* - 已加载数据缓存,避免重复请求
* - 竞态条件处理
*/
import { useState, useCallback, useRef, useEffect } from "react";
import axios from "@utils/axiosConfig";
import { logger } from "@utils/logger";
import type {
ApiKey,
ApiLoadingState,
DataState,
UseDeepAnalysisDataReturn,
} from "../types";
import { TAB_API_MAP } from "../types";
/** API 端点映射 */
const API_ENDPOINTS: Record<ApiKey, string> = {
comprehensive: "/api/company/comprehensive-analysis",
valueChain: "/api/company/value-chain-analysis",
keyFactors: "/api/company/key-factors-timeline",
industryRank: "/api/financial/industry-rank",
};
/** 初始数据状态 */
const initialDataState: DataState = {
comprehensive: null,
valueChain: null,
keyFactors: null,
industryRank: null,
};
/** 初始 loading 状态 */
const initialLoadingState: ApiLoadingState = {
comprehensive: false,
valueChain: false,
keyFactors: false,
industryRank: false,
};
/**
* 深度分析数据 Hook
*
* @param stockCode 股票代码
* @returns 数据、loading 状态、加载函数
*/
export const useDeepAnalysisData = (
stockCode: string
): UseDeepAnalysisDataReturn => {
// 数据状态
const [data, setData] = useState<DataState>(initialDataState);
// Loading 状态
const [loading, setLoading] = useState<ApiLoadingState>(initialLoadingState);
// 已加载的接口记录
const loadedApisRef = useRef<Record<ApiKey, boolean>>({
comprehensive: false,
valueChain: false,
keyFactors: false,
industryRank: false,
});
// 当前 stockCode用于竞态条件检测
const currentStockCodeRef = useRef(stockCode);
/**
* 加载指定 API 数据
*/
const loadApiData = useCallback(
async (apiKey: ApiKey) => {
if (!stockCode) return;
// 已加载则跳过
if (loadedApisRef.current[apiKey]) return;
// 设置 loading
setLoading((prev) => ({ ...prev, [apiKey]: true }));
try {
const endpoint = `${API_ENDPOINTS[apiKey]}/${stockCode}`;
const { data: response } = await axios.get(endpoint);
// 检查 stockCode 是否已变更(防止竞态)
if (currentStockCodeRef.current !== stockCode) return;
if (response.success) {
setData((prev) => ({ ...prev, [apiKey]: response.data }));
loadedApisRef.current[apiKey] = true;
}
} catch (err) {
logger.error("DeepAnalysis", `loadApiData:${apiKey}`, err, {
stockCode,
});
} finally {
// 清除 loading再次检查 stockCode
if (currentStockCodeRef.current === stockCode) {
setLoading((prev) => ({ ...prev, [apiKey]: false }));
}
}
},
[stockCode]
);
/**
* 根据 Tab 加载对应数据(支持一个 Tab 对应多个 API
*/
const loadTabData = useCallback(
(tabKey: string) => {
const apiKeys = TAB_API_MAP[tabKey];
if (apiKeys && apiKeys.length > 0) {
apiKeys.forEach((apiKey) => loadApiData(apiKey));
}
},
[loadApiData]
);
/**
* 重置所有数据
*/
const resetData = useCallback(() => {
setData(initialDataState);
setLoading(initialLoadingState);
loadedApisRef.current = {
comprehensive: false,
valueChain: false,
keyFactors: false,
industryRank: false,
};
}, []);
// stockCode 变更时重置并加载默认数据
useEffect(() => {
if (stockCode) {
currentStockCodeRef.current = stockCode;
resetData();
// 加载默认 Tab (strategy) 所需的所有数据
const defaultTabApis = TAB_API_MAP["strategy"];
if (defaultTabApis) {
defaultTabApis.forEach((apiKey) => loadApiData(apiKey));
}
}
}, [stockCode, loadApiData, resetData]);
return {
data,
loading,
loadTabData,
resetData,
};
};
export default useDeepAnalysisData;