- 新增 types.ts:API 类型定义、状态接口、Tab 映射常量 - 新增 hooks/useDeepAnalysisData.ts:提取数据获取逻辑 - 懒加载:按 Tab 按需请求 - 数据缓存:已加载数据不重复请求 - 竞态处理:stockCode 变更时防止旧请求覆盖 - 重写 index.tsx:memo 优化,代码行数 229 → 81 - 新增 README.md:组件文档 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
151 lines
3.7 KiB
TypeScript
151 lines
3.7 KiB
TypeScript
/**
|
||
* 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 加载对应数据
|
||
*/
|
||
const loadTabData = useCallback(
|
||
(tabKey: string) => {
|
||
const apiKey = TAB_API_MAP[tabKey];
|
||
if (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(comprehensive)
|
||
loadApiData('comprehensive');
|
||
}
|
||
}, [stockCode, loadApiData, resetData]);
|
||
|
||
return {
|
||
data,
|
||
loading,
|
||
loadTabData,
|
||
resetData,
|
||
};
|
||
};
|
||
|
||
export default useDeepAnalysisData;
|