From 77ea38e5c95619b1c0e605d41777b43ab59fc06d Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 22 Dec 2025 13:02:29 +0800 Subject: [PATCH] =?UTF-8?q?perf(hooks):=20=E4=BD=BF=E7=94=A8=20useRef=20?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E5=8A=A0=E8=BD=BD=E7=8A=B6=E6=80=81=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8D=20Tab=20=E5=88=87=E6=8D=A2=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用 useRef 替代 useState 跟踪 hasLoaded 状态 - Tab 切换回来时保持数据缓存,不重新发起请求 - stockCode 变化时重置加载状态,确保新股票正常加载 - useAnnouncementsData 支持 refreshKey 强制刷新 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../hooks/useAnnouncementsData.ts | 34 ++++++++++++++++--- .../CompanyOverview/hooks/useBranchesData.ts | 25 +++++++++++--- .../hooks/useDisclosureData.ts | 25 +++++++++++--- .../hooks/useManagementData.ts | 25 +++++++++++--- .../hooks/useShareholderData.ts | 25 +++++++++++--- 5 files changed, 109 insertions(+), 25 deletions(-) diff --git a/src/views/Company/components/CompanyOverview/hooks/useAnnouncementsData.ts b/src/views/Company/components/CompanyOverview/hooks/useAnnouncementsData.ts index e6ff2db8..473a9674 100644 --- a/src/views/Company/components/CompanyOverview/hooks/useAnnouncementsData.ts +++ b/src/views/Company/components/CompanyOverview/hooks/useAnnouncementsData.ts @@ -1,7 +1,7 @@ // src/views/Company/components/CompanyOverview/hooks/useAnnouncementsData.ts // 公告数据 Hook - 用于公司公告 Tab -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import { logger } from "@utils/logger"; import axios from "@utils/axiosConfig"; import type { Announcement } from "../types"; @@ -39,7 +39,11 @@ export const useAnnouncementsData = (options: UseAnnouncementsDataOptions): UseA const [announcements, setAnnouncements] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const [hasLoaded, setHasLoaded] = useState(false); + // 使用 ref 跟踪是否已加载,避免 Tab 切换时重复请求 + const hasLoadedRef = useRef(false); + // 记录上次加载的 stockCode 和 refreshKey + const lastStockCodeRef = useRef(undefined); + const lastRefreshKeyRef = useRef(undefined); useEffect(() => { // 只有 enabled 且有 stockCode 时才请求 @@ -48,6 +52,26 @@ export const useAnnouncementsData = (options: UseAnnouncementsDataOptions): UseA return; } + // stockCode 或 refreshKey 变化时重置加载状态 + if (lastStockCodeRef.current !== stockCode || lastRefreshKeyRef.current !== refreshKey) { + // refreshKey 变化时强制重新加载 + if (lastRefreshKeyRef.current !== refreshKey && lastRefreshKeyRef.current !== undefined) { + hasLoadedRef.current = false; + } + // stockCode 变化时重置 + if (lastStockCodeRef.current !== stockCode) { + hasLoadedRef.current = false; + } + lastStockCodeRef.current = stockCode; + lastRefreshKeyRef.current = refreshKey; + } + + // 如果已经加载过数据,不再重新请求(Tab 切换回来时保持缓存) + if (hasLoadedRef.current) { + setLoading(false); + return; + } + const controller = new AbortController(); const loadData = async () => { @@ -66,7 +90,7 @@ export const useAnnouncementsData = (options: UseAnnouncementsDataOptions): UseA setError("加载公告数据失败"); } setLoading(false); - setHasLoaded(true); + hasLoadedRef.current = true; } catch (err: any) { // 请求被取消时,不更新任何状态 if (err.name === "CanceledError") { @@ -75,7 +99,7 @@ export const useAnnouncementsData = (options: UseAnnouncementsDataOptions): UseA logger.error("useAnnouncementsData", "loadData", err, { stockCode }); setError("网络请求失败"); setLoading(false); - setHasLoaded(true); + hasLoadedRef.current = true; } }; @@ -83,7 +107,7 @@ export const useAnnouncementsData = (options: UseAnnouncementsDataOptions): UseA return () => controller.abort(); }, [stockCode, enabled, refreshKey]); - const isLoading = loading || (enabled && !hasLoaded && !error); + const isLoading = loading || (enabled && !hasLoadedRef.current && !error); return { announcements, loading: isLoading, error }; }; diff --git a/src/views/Company/components/CompanyOverview/hooks/useBranchesData.ts b/src/views/Company/components/CompanyOverview/hooks/useBranchesData.ts index 65299387..5ed23bd6 100644 --- a/src/views/Company/components/CompanyOverview/hooks/useBranchesData.ts +++ b/src/views/Company/components/CompanyOverview/hooks/useBranchesData.ts @@ -1,7 +1,7 @@ // src/views/Company/components/CompanyOverview/hooks/useBranchesData.ts // 分支机构数据 Hook - 用于分支机构 Tab -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import { logger } from "@utils/logger"; import axios from "@utils/axiosConfig"; import type { Branch } from "../types"; @@ -36,7 +36,10 @@ export const useBranchesData = (options: UseBranchesDataOptions): UseBranchesDat const [branches, setBranches] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const [hasLoaded, setHasLoaded] = useState(false); + // 使用 ref 跟踪是否已加载,避免 Tab 切换时重复请求 + const hasLoadedRef = useRef(false); + // 记录上次加载的 stockCode,stockCode 变化时需要重新加载 + const lastStockCodeRef = useRef(undefined); useEffect(() => { if (!enabled || !stockCode) { @@ -44,6 +47,18 @@ export const useBranchesData = (options: UseBranchesDataOptions): UseBranchesDat 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 () => { @@ -62,7 +77,7 @@ export const useBranchesData = (options: UseBranchesDataOptions): UseBranchesDat setError("加载分支机构数据失败"); } setLoading(false); - setHasLoaded(true); + hasLoadedRef.current = true; } catch (err: any) { // 请求被取消时,不更新任何状态 if (err.name === "CanceledError") { @@ -71,7 +86,7 @@ export const useBranchesData = (options: UseBranchesDataOptions): UseBranchesDat logger.error("useBranchesData", "loadData", err, { stockCode }); setError("网络请求失败"); setLoading(false); - setHasLoaded(true); + hasLoadedRef.current = true; } }; @@ -79,7 +94,7 @@ export const useBranchesData = (options: UseBranchesDataOptions): UseBranchesDat return () => controller.abort(); }, [stockCode, enabled]); - const isLoading = loading || (enabled && !hasLoaded && !error); + const isLoading = loading || (enabled && !hasLoadedRef.current && !error); return { branches, loading: isLoading, error }; }; diff --git a/src/views/Company/components/CompanyOverview/hooks/useDisclosureData.ts b/src/views/Company/components/CompanyOverview/hooks/useDisclosureData.ts index 38e4f229..4cf9f0ea 100644 --- a/src/views/Company/components/CompanyOverview/hooks/useDisclosureData.ts +++ b/src/views/Company/components/CompanyOverview/hooks/useDisclosureData.ts @@ -1,7 +1,7 @@ // src/views/Company/components/CompanyOverview/hooks/useDisclosureData.ts // 披露日程数据 Hook - 用于工商信息 Tab -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import { logger } from "@utils/logger"; import axios from "@utils/axiosConfig"; import type { DisclosureSchedule } from "../types"; @@ -36,7 +36,10 @@ export const useDisclosureData = (options: UseDisclosureDataOptions): UseDisclos const [disclosureSchedule, setDisclosureSchedule] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const [hasLoaded, setHasLoaded] = useState(false); + // 使用 ref 跟踪是否已加载,避免 Tab 切换时重复请求 + const hasLoadedRef = useRef(false); + // 记录上次加载的 stockCode,stockCode 变化时需要重新加载 + const lastStockCodeRef = useRef(undefined); useEffect(() => { // 只有 enabled 且有 stockCode 时才请求 @@ -45,6 +48,18 @@ export const useDisclosureData = (options: UseDisclosureDataOptions): UseDisclos 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 () => { @@ -63,7 +78,7 @@ export const useDisclosureData = (options: UseDisclosureDataOptions): UseDisclos setError("加载披露日程数据失败"); } setLoading(false); - setHasLoaded(true); + hasLoadedRef.current = true; } catch (err: any) { // 请求被取消时,不更新任何状态 if (err.name === "CanceledError") { @@ -72,7 +87,7 @@ export const useDisclosureData = (options: UseDisclosureDataOptions): UseDisclos logger.error("useDisclosureData", "loadData", err, { stockCode }); setError("网络请求失败"); setLoading(false); - setHasLoaded(true); + hasLoadedRef.current = true; } }; @@ -80,7 +95,7 @@ export const useDisclosureData = (options: UseDisclosureDataOptions): UseDisclos return () => controller.abort(); }, [stockCode, enabled]); - const isLoading = loading || (enabled && !hasLoaded && !error); + const isLoading = loading || (enabled && !hasLoadedRef.current && !error); return { disclosureSchedule, loading: isLoading, error }; }; diff --git a/src/views/Company/components/CompanyOverview/hooks/useManagementData.ts b/src/views/Company/components/CompanyOverview/hooks/useManagementData.ts index 62590e77..ca3d3f72 100644 --- a/src/views/Company/components/CompanyOverview/hooks/useManagementData.ts +++ b/src/views/Company/components/CompanyOverview/hooks/useManagementData.ts @@ -1,7 +1,7 @@ // src/views/Company/components/CompanyOverview/hooks/useManagementData.ts // 管理团队数据 Hook - 用于管理团队 Tab -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import { logger } from "@utils/logger"; import axios from "@utils/axiosConfig"; import type { Management } from "../types"; @@ -36,7 +36,10 @@ export const useManagementData = (options: UseManagementDataOptions): UseManagem const [management, setManagement] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const [hasLoaded, setHasLoaded] = useState(false); + // 使用 ref 跟踪是否已加载,避免 Tab 切换时重复请求 + const hasLoadedRef = useRef(false); + // 记录上次加载的 stockCode,stockCode 变化时需要重新加载 + const lastStockCodeRef = useRef(undefined); useEffect(() => { // 只有 enabled 且有 stockCode 时才请求 @@ -45,6 +48,18 @@ export const useManagementData = (options: UseManagementDataOptions): UseManagem 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 () => { @@ -63,7 +78,7 @@ export const useManagementData = (options: UseManagementDataOptions): UseManagem setError("加载管理团队数据失败"); } setLoading(false); - setHasLoaded(true); + hasLoadedRef.current = true; } catch (err: any) { // 请求被取消时,不更新任何状态 if (err.name === "CanceledError") { @@ -72,7 +87,7 @@ export const useManagementData = (options: UseManagementDataOptions): UseManagem logger.error("useManagementData", "loadData", err, { stockCode }); setError("网络请求失败"); setLoading(false); - setHasLoaded(true); + hasLoadedRef.current = true; } }; @@ -82,7 +97,7 @@ export const useManagementData = (options: UseManagementDataOptions): UseManagem // 派生 loading 状态:enabled 但尚未完成首次加载时,视为 loading // 这样可以在渲染时同步判断,避免 useEffect 异步导致的空状态闪烁 - const isLoading = loading || (enabled && !hasLoaded && !error); + const isLoading = loading || (enabled && !hasLoadedRef.current && !error); return { management, loading: isLoading, error }; }; diff --git a/src/views/Company/components/CompanyOverview/hooks/useShareholderData.ts b/src/views/Company/components/CompanyOverview/hooks/useShareholderData.ts index 6c47b62d..b9867adb 100644 --- a/src/views/Company/components/CompanyOverview/hooks/useShareholderData.ts +++ b/src/views/Company/components/CompanyOverview/hooks/useShareholderData.ts @@ -1,7 +1,7 @@ // src/views/Company/components/CompanyOverview/hooks/useShareholderData.ts // 股权结构数据 Hook - 用于股权结构 Tab -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import { logger } from "@utils/logger"; import axios from "@utils/axiosConfig"; import type { ActualControl, Concentration, Shareholder } from "../types"; @@ -42,7 +42,10 @@ export const useShareholderData = (options: UseShareholderDataOptions): UseShare const [topCirculationShareholders, setTopCirculationShareholders] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const [hasLoaded, setHasLoaded] = useState(false); + // 使用 ref 跟踪是否已加载,避免 Tab 切换时重复请求 + const hasLoadedRef = useRef(false); + // 记录上次加载的 stockCode,stockCode 变化时需要重新加载 + const lastStockCodeRef = useRef(undefined); useEffect(() => { // 只有 enabled 且有 stockCode 时才请求 @@ -51,6 +54,18 @@ export const useShareholderData = (options: UseShareholderDataOptions): UseShare 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 () => { @@ -75,7 +90,7 @@ export const useShareholderData = (options: UseShareholderDataOptions): UseShare if (shareholdersRes.success) setTopShareholders(shareholdersRes.data); if (circulationRes.success) setTopCirculationShareholders(circulationRes.data); setLoading(false); - setHasLoaded(true); + hasLoadedRef.current = true; } catch (err: any) { // 请求被取消时,不更新任何状态 if (err.name === "CanceledError") { @@ -84,7 +99,7 @@ export const useShareholderData = (options: UseShareholderDataOptions): UseShare logger.error("useShareholderData", "loadData", err, { stockCode }); setError("加载股权结构数据失败"); setLoading(false); - setHasLoaded(true); + hasLoadedRef.current = true; } }; @@ -92,7 +107,7 @@ export const useShareholderData = (options: UseShareholderDataOptions): UseShare return () => controller.abort(); }, [stockCode, enabled]); - const isLoading = loading || (enabled && !hasLoaded && !error); + const isLoading = loading || (enabled && !hasLoadedRef.current && !error); return { actualControl,