From 003eab4f21f5ea315029dbef13abce546830cdf5 Mon Sep 17 00:00:00 2001 From: oudecheng <13802883547@139.com> Date: Thu, 30 Jul 2026 18:56:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20=E4=BF=AE=E5=A4=8D=E7=82=B9?= =?UTF-8?q?=E5=87=BB=E8=AE=BE=E7=BD=AE=E5=90=8E=E7=99=BD=E5=B1=8F=EF=BC=88?= =?UTF-8?q?React=20hooks=20=E9=A1=BA=E5=BA=8F=E8=BF=9D=E8=A7=84=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 子模态框 ESC 处理的 useEffect 被放在 if(loading)/if(!config) 条件 return 之后,导致首次渲染(loading=true)时不执行该 hook,config 加载后重新渲染才执行 → hooks 数量不一致 → React error #310 白屏崩溃。 修复:将 useEffect 移到所有条件 return 之前,与其他 hooks 放在一起。 --- web/src/components/Settings/ConfigPage.tsx | 32 ++++++++++++---------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/web/src/components/Settings/ConfigPage.tsx b/web/src/components/Settings/ConfigPage.tsx index cccad71..19d7070 100644 --- a/web/src/components/Settings/ConfigPage.tsx +++ b/web/src/components/Settings/ConfigPage.tsx @@ -200,6 +200,23 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage return () => document.removeEventListener('keydown', h) }, [handleClose]) + // 子模态框打开时,ESC 键仅关闭子模态框(阻止冒泡到 ConfigPage 全局 ESC,避免关闭整个配置页) + // 必须放在所有条件 return 之前,否则 loading 首次渲染时不执行此 hook, + // config 加载后重新渲染才执行 → hooks 数量不一致 → React 崩溃 + useEffect(() => { + if (!editingExpert && !editingSubagent) return + const handler = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + e.stopPropagation() + e.preventDefault() + setEditingExpert(null) + setEditingSubagent(null) + } + } + window.addEventListener('keydown', handler, true) + return () => window.removeEventListener('keydown', handler, true) + }, [editingExpert, editingSubagent]) + const update = useCallback((key: K, value: AppConfig[K]) => { setConfig(prev => prev ? { ...prev, [key]: value } : prev) setDirty(true) @@ -904,21 +921,6 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage const toolEmptyHint = toolListLoading ? '加载中...' : '未发现任何工具' const subagentEmptyHint = subagentListLoading ? '加载中...' : '未发现任何子代理' - // 子模态框打开时,ESC 键仅关闭子模态框(阻止冒泡到 ConfigPage 全局 ESC,避免关闭整个配置页) - useEffect(() => { - if (!editingExpert && !editingSubagent) return - const handler = (e: KeyboardEvent) => { - if (e.key === 'Escape') { - e.stopPropagation() - e.preventDefault() - setEditingExpert(null) - setEditingSubagent(null) - } - } - window.addEventListener('keydown', handler, true) - return () => window.removeEventListener('keydown', handler, true) - }, [editingExpert, editingSubagent]) - const renderExpertModal = () => { if (!editingExpert) return null const isEdit = editingExpert.mode === 'edit'