perf(CompanyOverview): hooks 支持 enabled 延迟加载和刷新

- 所有 hooks 参数改为 options 对象形式
- 新增 enabled 参数支持延迟加载
- 新增 refreshKey 参数支持手动刷新
- 智能初始化 loading 状态,避免首次渲染闪现空状态

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-19 18:53:30 +08:00
parent 672e746a26
commit 298ac5a335
6 changed files with 132 additions and 38 deletions

View File

@@ -11,6 +11,15 @@ interface ApiResponse<T> {
data: T; data: T;
} }
// 支持延迟加载的配置选项
interface UseAnnouncementsDataOptions {
stockCode?: string;
/** 是否启用数据加载,默认 true */
enabled?: boolean;
/** 刷新标识,变化时触发重新请求 */
refreshKey?: number;
}
interface UseAnnouncementsDataResult { interface UseAnnouncementsDataResult {
announcements: Announcement[]; announcements: Announcement[];
loading: boolean; loading: boolean;
@@ -18,16 +27,22 @@ interface UseAnnouncementsDataResult {
} }
/** /**
* 公告数据 Hook * 公告数据 Hook(支持延迟加载和刷新)
* @param stockCode - 股票代码 * @param options - 配置选项
* @param options.stockCode - 股票代码
* @param options.enabled - 是否启用数据加载,默认 true
* @param options.refreshKey - 刷新标识,变化时触发重新请求
*/ */
export const useAnnouncementsData = (stockCode?: string): UseAnnouncementsDataResult => { export const useAnnouncementsData = (options: UseAnnouncementsDataOptions): UseAnnouncementsDataResult => {
const { stockCode, enabled = true, refreshKey } = options;
const [announcements, setAnnouncements] = useState<Announcement[]>([]); const [announcements, setAnnouncements] = useState<Announcement[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
if (!stockCode) return; // 只有 enabled 且有 stockCode 时才请求
if (!enabled || !stockCode) return;
const controller = new AbortController(); const controller = new AbortController();
@@ -57,7 +72,7 @@ export const useAnnouncementsData = (stockCode?: string): UseAnnouncementsDataRe
loadData(); loadData();
return () => controller.abort(); return () => controller.abort();
}, [stockCode]); }, [stockCode, enabled, refreshKey]);
return { announcements, loading, error }; return { announcements, loading, error };
}; };

View File

@@ -11,6 +11,13 @@ interface ApiResponse<T> {
data: T; data: T;
} }
// 支持延迟加载的配置选项
interface UseBasicInfoOptions {
stockCode?: string;
/** 是否启用数据加载,默认 true */
enabled?: boolean;
}
interface UseBasicInfoResult { interface UseBasicInfoResult {
basicInfo: BasicInfo | null; basicInfo: BasicInfo | null;
loading: boolean; loading: boolean;
@@ -18,16 +25,22 @@ interface UseBasicInfoResult {
} }
/** /**
* 公司基本信息 Hook * 公司基本信息 Hook(支持延迟加载)
* @param stockCode - 股票代码 * @param options - 配置选项
* @param options.stockCode - 股票代码
* @param options.enabled - 是否启用数据加载,默认 true
*/ */
export const useBasicInfo = (stockCode?: string): UseBasicInfoResult => { export const useBasicInfo = (options: UseBasicInfoOptions): UseBasicInfoResult => {
const { stockCode, enabled = true } = options;
const [basicInfo, setBasicInfo] = useState<BasicInfo | null>(null); const [basicInfo, setBasicInfo] = useState<BasicInfo | null>(null);
const [loading, setLoading] = useState(false); // 智能初始化 loading当需要加载数据时初始值为 true避免首次渲染闪现空状态
const [loading, setLoading] = useState(() => enabled && !!stockCode);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
if (!stockCode) return; // 只有 enabled 且有 stockCode 时才请求
if (!enabled || !stockCode) return;
const controller = new AbortController(); const controller = new AbortController();
@@ -57,7 +70,7 @@ export const useBasicInfo = (stockCode?: string): UseBasicInfoResult => {
loadData(); loadData();
return () => controller.abort(); return () => controller.abort();
}, [stockCode]); }, [stockCode, enabled]);
return { basicInfo, loading, error }; return { basicInfo, loading, error };
}; };

View File

@@ -11,6 +11,13 @@ interface ApiResponse<T> {
data: T; data: T;
} }
// 支持延迟加载的配置选项
interface UseBranchesDataOptions {
stockCode?: string;
/** 是否启用数据加载,默认 true */
enabled?: boolean;
}
interface UseBranchesDataResult { interface UseBranchesDataResult {
branches: Branch[]; branches: Branch[];
loading: boolean; loading: boolean;
@@ -18,16 +25,24 @@ interface UseBranchesDataResult {
} }
/** /**
* 分支机构数据 Hook * 分支机构数据 Hook(支持延迟加载)
* @param stockCode - 股票代码 * @param options - 配置选项
* @param options.stockCode - 股票代码
* @param options.enabled - 是否启用数据加载,默认 true
*/ */
export const useBranchesData = (stockCode?: string): UseBranchesDataResult => { export const useBranchesData = (options: UseBranchesDataOptions): UseBranchesDataResult => {
const { stockCode, enabled = true } = options;
const [branches, setBranches] = useState<Branch[]>([]); const [branches, setBranches] = useState<Branch[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [hasLoaded, setHasLoaded] = useState(false);
useEffect(() => { useEffect(() => {
if (!stockCode) return; if (!enabled || !stockCode) {
setLoading(false);
return;
}
const controller = new AbortController(); const controller = new AbortController();
@@ -46,18 +61,25 @@ export const useBranchesData = (stockCode?: string): UseBranchesDataResult => {
} else { } else {
setError("加载分支机构数据失败"); setError("加载分支机构数据失败");
} }
setLoading(false);
setHasLoaded(true);
} catch (err: any) { } catch (err: any) {
if (err.name === "CanceledError") return; // 请求被取消时,不更新任何状态
if (err.name === "CanceledError") {
return;
}
logger.error("useBranchesData", "loadData", err, { stockCode }); logger.error("useBranchesData", "loadData", err, { stockCode });
setError("网络请求失败"); setError("网络请求失败");
} finally {
setLoading(false); setLoading(false);
setHasLoaded(true);
} }
}; };
loadData(); loadData();
return () => controller.abort(); return () => controller.abort();
}, [stockCode]); }, [stockCode, enabled]);
return { branches, loading, error }; const isLoading = loading || (enabled && !hasLoaded && !error);
return { branches, loading: isLoading, error };
}; };

View File

@@ -11,6 +11,13 @@ interface ApiResponse<T> {
data: T; data: T;
} }
// 支持延迟加载的配置选项
interface UseDisclosureDataOptions {
stockCode?: string;
/** 是否启用数据加载,默认 true */
enabled?: boolean;
}
interface UseDisclosureDataResult { interface UseDisclosureDataResult {
disclosureSchedule: DisclosureSchedule[]; disclosureSchedule: DisclosureSchedule[];
loading: boolean; loading: boolean;
@@ -18,16 +25,21 @@ interface UseDisclosureDataResult {
} }
/** /**
* 披露日程数据 Hook * 披露日程数据 Hook(支持延迟加载)
* @param stockCode - 股票代码 * @param options - 配置选项
* @param options.stockCode - 股票代码
* @param options.enabled - 是否启用数据加载,默认 true
*/ */
export const useDisclosureData = (stockCode?: string): UseDisclosureDataResult => { export const useDisclosureData = (options: UseDisclosureDataOptions): UseDisclosureDataResult => {
const { stockCode, enabled = true } = options;
const [disclosureSchedule, setDisclosureSchedule] = useState<DisclosureSchedule[]>([]); const [disclosureSchedule, setDisclosureSchedule] = useState<DisclosureSchedule[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
if (!stockCode) return; // 只有 enabled 且有 stockCode 时才请求
if (!enabled || !stockCode) return;
const controller = new AbortController(); const controller = new AbortController();
@@ -57,7 +69,7 @@ export const useDisclosureData = (stockCode?: string): UseDisclosureDataResult =
loadData(); loadData();
return () => controller.abort(); return () => controller.abort();
}, [stockCode]); }, [stockCode, enabled]);
return { disclosureSchedule, loading, error }; return { disclosureSchedule, loading, error };
}; };

View File

@@ -11,6 +11,13 @@ interface ApiResponse<T> {
data: T; data: T;
} }
// 支持延迟加载的配置选项
interface UseManagementDataOptions {
stockCode?: string;
/** 是否启用数据加载,默认 true */
enabled?: boolean;
}
interface UseManagementDataResult { interface UseManagementDataResult {
management: Management[]; management: Management[];
loading: boolean; loading: boolean;
@@ -18,16 +25,23 @@ interface UseManagementDataResult {
} }
/** /**
* 管理团队数据 Hook * 管理团队数据 Hook(支持延迟加载)
* @param stockCode - 股票代码 * @param options - 配置选项
* @param options.stockCode - 股票代码
* @param options.enabled - 是否启用数据加载,默认 true
*/ */
export const useManagementData = (stockCode?: string): UseManagementDataResult => { export const useManagementData = (options: UseManagementDataOptions): UseManagementDataResult => {
const { stockCode, enabled = true } = options;
const [management, setManagement] = useState<Management[]>([]); const [management, setManagement] = useState<Management[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(() => enabled && !!stockCode);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
// 记录是否已完成首次加载,用于派生 loading 状态
const [hasLoaded, setHasLoaded] = useState(false);
useEffect(() => { useEffect(() => {
if (!stockCode) return; // 只有 enabled 且有 stockCode 时才请求
if (!enabled || !stockCode) return;
const controller = new AbortController(); const controller = new AbortController();
@@ -52,12 +66,17 @@ export const useManagementData = (stockCode?: string): UseManagementDataResult =
setError("网络请求失败"); setError("网络请求失败");
} finally { } finally {
setLoading(false); setLoading(false);
setHasLoaded(true);
} }
}; };
loadData(); loadData();
return () => controller.abort(); return () => controller.abort();
}, [stockCode]); }, [stockCode, enabled]);
return { management, loading, error }; // 派生 loading 状态enabled 但尚未完成首次加载时,视为 loading
// 这样可以在渲染时同步判断,避免 useEffect 异步导致的空状态闪烁
const isLoading = loading || (enabled && !hasLoaded && !error);
return { management, loading: isLoading, error };
}; };

View File

@@ -11,6 +11,13 @@ interface ApiResponse<T> {
data: T; data: T;
} }
// 支持延迟加载的配置选项
interface UseShareholderDataOptions {
stockCode?: string;
/** 是否启用数据加载,默认 true */
enabled?: boolean;
}
interface UseShareholderDataResult { interface UseShareholderDataResult {
actualControl: ActualControl[]; actualControl: ActualControl[];
concentration: Concentration[]; concentration: Concentration[];
@@ -21,19 +28,25 @@ interface UseShareholderDataResult {
} }
/** /**
* 股权结构数据 Hook * 股权结构数据 Hook(支持延迟加载)
* @param stockCode - 股票代码 * @param options - 配置选项
* @param options.stockCode - 股票代码
* @param options.enabled - 是否启用数据加载,默认 true
*/ */
export const useShareholderData = (stockCode?: string): UseShareholderDataResult => { export const useShareholderData = (options: UseShareholderDataOptions): UseShareholderDataResult => {
const { stockCode, enabled = true } = options;
const [actualControl, setActualControl] = useState<ActualControl[]>([]); const [actualControl, setActualControl] = useState<ActualControl[]>([]);
const [concentration, setConcentration] = useState<Concentration[]>([]); const [concentration, setConcentration] = useState<Concentration[]>([]);
const [topShareholders, setTopShareholders] = useState<Shareholder[]>([]); const [topShareholders, setTopShareholders] = useState<Shareholder[]>([]);
const [topCirculationShareholders, setTopCirculationShareholders] = useState<Shareholder[]>([]); const [topCirculationShareholders, setTopCirculationShareholders] = useState<Shareholder[]>([]);
const [loading, setLoading] = useState(false); // 智能初始化 loading当需要加载数据时初始值为 true避免首次渲染闪现空状态
const [loading, setLoading] = useState(() => enabled && !!stockCode);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
if (!stockCode) return; // 只有 enabled 且有 stockCode 时才请求
if (!enabled || !stockCode) return;
const controller = new AbortController(); const controller = new AbortController();
@@ -69,7 +82,7 @@ export const useShareholderData = (stockCode?: string): UseShareholderDataResult
loadData(); loadData();
return () => controller.abort(); return () => controller.abort();
}, [stockCode]); }, [stockCode, enabled]);
return { return {
actualControl, actualControl,