From 455f46caa05e0d78634918f9d1b125a8767e11f9 Mon Sep 17 00:00:00 2001 From: TaskPool Date: Sat, 8 Aug 2026 03:15:51 +0800 Subject: [PATCH] fix(ssr): avoid accessing localStorage during SSR initialization - Initialize activeInterconnectNodeId with empty string - Read from localStorage only in client-side - Defer selected state initialization in NodeSwitcher to useEffect --- web/src/api/index.ts | 12 ++++++++++-- web/src/components/business/NodeSwitcher.tsx | 4 +++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/web/src/api/index.ts b/web/src/api/index.ts index f100b69..53adc1a 100644 --- a/web/src/api/index.ts +++ b/web/src/api/index.ts @@ -65,8 +65,16 @@ export interface MonitorStats { }[] } } -export let activeInterconnectNodeId = localStorage.getItem('activeInterconnectNodeId') || '' -export let activeInterconnectNodeName = localStorage.getItem('activeInterconnectNodeName') || '' + +// 初始化时使用空字符串,避免 SSR 时访问 localStorage +export let activeInterconnectNodeId = '' +export let activeInterconnectNodeName = '' + +// 在客户端初始化时从 localStorage 读取 +if (typeof window !== 'undefined') { + activeInterconnectNodeId = localStorage.getItem('activeInterconnectNodeId') || '' + activeInterconnectNodeName = localStorage.getItem('activeInterconnectNodeName') || '' +} export function setActiveInterconnectNodeId(id: string, name?: string) { activeInterconnectNodeId = id diff --git a/web/src/components/business/NodeSwitcher.tsx b/web/src/components/business/NodeSwitcher.tsx index d8c0444..781993d 100644 --- a/web/src/components/business/NodeSwitcher.tsx +++ b/web/src/components/business/NodeSwitcher.tsx @@ -13,7 +13,7 @@ import { cn } from '@/lib/utils' export function NodeSwitcher() { const [nodes, setNodes] = useState([]) - const [selected, setSelected] = useState(activeInterconnectNodeId || 'local') + const [selected, setSelected] = useState('local') const [isMaster, setIsMaster] = useState(false) const [open, setOpen] = useState(false) const [menuStyle, setMenuStyle] = useState({}) @@ -22,6 +22,8 @@ export function NodeSwitcher() { useEffect(() => { setMounted(true) + // 在客户端初始化时从 localStorage 读取选中的节点 + setSelected(activeInterconnectNodeId || 'local') }, []) const fetchNodes = useCallback(async () => {