- 使用 useRef 替代 useState 跟踪 hasLoaded 状态 - Tab 切换回来时保持数据缓存,不重新发起请求 - stockCode 变化时重置加载状态,确保新股票正常加载 - useAnnouncementsData 支持 refreshKey 强制刷新 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
121 lines
4.2 KiB
TypeScript
121 lines
4.2 KiB
TypeScript
// src/views/Company/components/CompanyOverview/hooks/useShareholderData.ts
|
||
// 股权结构数据 Hook - 用于股权结构 Tab
|
||
|
||
import { useState, useEffect, useRef } from "react";
|
||
import { logger } from "@utils/logger";
|
||
import axios from "@utils/axiosConfig";
|
||
import type { ActualControl, Concentration, Shareholder } from "../types";
|
||
|
||
interface ApiResponse<T> {
|
||
success: boolean;
|
||
data: T;
|
||
}
|
||
|
||
// 支持延迟加载的配置选项
|
||
interface UseShareholderDataOptions {
|
||
stockCode?: string;
|
||
/** 是否启用数据加载,默认 true */
|
||
enabled?: boolean;
|
||
}
|
||
|
||
interface UseShareholderDataResult {
|
||
actualControl: ActualControl[];
|
||
concentration: Concentration[];
|
||
topShareholders: Shareholder[];
|
||
topCirculationShareholders: Shareholder[];
|
||
loading: boolean;
|
||
error: string | null;
|
||
}
|
||
|
||
/**
|
||
* 股权结构数据 Hook(支持延迟加载)
|
||
* @param options - 配置选项
|
||
* @param options.stockCode - 股票代码
|
||
* @param options.enabled - 是否启用数据加载,默认 true
|
||
*/
|
||
export const useShareholderData = (options: UseShareholderDataOptions): UseShareholderDataResult => {
|
||
const { stockCode, enabled = true } = options;
|
||
|
||
const [actualControl, setActualControl] = useState<ActualControl[]>([]);
|
||
const [concentration, setConcentration] = useState<Concentration[]>([]);
|
||
const [topShareholders, setTopShareholders] = useState<Shareholder[]>([]);
|
||
const [topCirculationShareholders, setTopCirculationShareholders] = useState<Shareholder[]>([]);
|
||
const [loading, setLoading] = useState(true);
|
||
const [error, setError] = useState<string | null>(null);
|
||
// 使用 ref 跟踪是否已加载,避免 Tab 切换时重复请求
|
||
const hasLoadedRef = useRef(false);
|
||
// 记录上次加载的 stockCode,stockCode 变化时需要重新加载
|
||
const lastStockCodeRef = useRef<string | undefined>(undefined);
|
||
|
||
useEffect(() => {
|
||
// 只有 enabled 且有 stockCode 时才请求
|
||
if (!enabled || !stockCode) {
|
||
setLoading(false);
|
||
return;
|
||
}
|
||
|
||
// stockCode 变化时重置加载状态
|
||
if (lastStockCodeRef.current !== stockCode) {
|
||
hasLoadedRef.current = false;
|
||
lastStockCodeRef.current = stockCode;
|
||
}
|
||
|
||
// 如果已经加载过数据,不再重新请求(Tab 切换回来时保持缓存)
|
||
if (hasLoadedRef.current) {
|
||
setLoading(false);
|
||
return;
|
||
}
|
||
|
||
const controller = new AbortController();
|
||
|
||
const loadData = async () => {
|
||
setLoading(true);
|
||
setError(null);
|
||
|
||
try {
|
||
const [
|
||
{ data: actualRes },
|
||
{ data: concentrationRes },
|
||
{ data: shareholdersRes },
|
||
{ data: circulationRes },
|
||
] = await Promise.all([
|
||
axios.get<ApiResponse<ActualControl[]>>(`/api/stock/${stockCode}/actual-control`, { signal: controller.signal }),
|
||
axios.get<ApiResponse<Concentration[]>>(`/api/stock/${stockCode}/concentration`, { signal: controller.signal }),
|
||
axios.get<ApiResponse<Shareholder[]>>(`/api/stock/${stockCode}/top-shareholders?limit=10`, { signal: controller.signal }),
|
||
axios.get<ApiResponse<Shareholder[]>>(`/api/stock/${stockCode}/top-circulation-shareholders?limit=10`, { signal: controller.signal }),
|
||
]);
|
||
|
||
if (actualRes.success) setActualControl(actualRes.data);
|
||
if (concentrationRes.success) setConcentration(concentrationRes.data);
|
||
if (shareholdersRes.success) setTopShareholders(shareholdersRes.data);
|
||
if (circulationRes.success) setTopCirculationShareholders(circulationRes.data);
|
||
setLoading(false);
|
||
hasLoadedRef.current = true;
|
||
} catch (err: any) {
|
||
// 请求被取消时,不更新任何状态
|
||
if (err.name === "CanceledError") {
|
||
return;
|
||
}
|
||
logger.error("useShareholderData", "loadData", err, { stockCode });
|
||
setError("加载股权结构数据失败");
|
||
setLoading(false);
|
||
hasLoadedRef.current = true;
|
||
}
|
||
};
|
||
|
||
loadData();
|
||
return () => controller.abort();
|
||
}, [stockCode, enabled]);
|
||
|
||
const isLoading = loading || (enabled && !hasLoadedRef.current && !error);
|
||
|
||
return {
|
||
actualControl,
|
||
concentration,
|
||
topShareholders,
|
||
topCirculationShareholders,
|
||
loading: isLoading,
|
||
error,
|
||
};
|
||
};
|