// 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 { 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([]); const [concentration, setConcentration] = useState([]); const [topShareholders, setTopShareholders] = useState([]); const [topCirculationShareholders, setTopCirculationShareholders] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // 使用 ref 跟踪是否已加载,避免 Tab 切换时重复请求 const hasLoadedRef = useRef(false); // 记录上次加载的 stockCode,stockCode 变化时需要重新加载 const lastStockCodeRef = useRef(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>(`/api/stock/${stockCode}/actual-control`, { signal: controller.signal }), axios.get>(`/api/stock/${stockCode}/concentration`, { signal: controller.signal }), axios.get>(`/api/stock/${stockCode}/top-shareholders?limit=10`, { signal: controller.signal }), axios.get>(`/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, }; };