fix(web): 修复点击设置后白屏(React hooks 顺序违规)

子模态框 ESC 处理的 useEffect 被放在 if(loading)/if(!config) 条件 return 之后,导致首次渲染(loading=true)时不执行该 hook,config 加载后重新渲染才执行 → hooks 数量不一致 → React error #310 白屏崩溃。

修复:将 useEffect 移到所有条件 return 之前,与其他 hooks 放在一起。
This commit is contained in:
oudecheng 2026-07-30 18:56:43 +08:00
parent 951e31aca4
commit 003eab4f21

View File

@ -200,6 +200,23 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage
return () => document.removeEventListener('keydown', h) return () => document.removeEventListener('keydown', h)
}, [handleClose]) }, [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(<K extends keyof AppConfig>(key: K, value: AppConfig[K]) => { const update = useCallback(<K extends keyof AppConfig>(key: K, value: AppConfig[K]) => {
setConfig(prev => prev ? { ...prev, [key]: value } : prev) setConfig(prev => prev ? { ...prev, [key]: value } : prev)
setDirty(true) setDirty(true)
@ -904,21 +921,6 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage
const toolEmptyHint = toolListLoading ? '加载中...' : '未发现任何工具' const toolEmptyHint = toolListLoading ? '加载中...' : '未发现任何工具'
const subagentEmptyHint = subagentListLoading ? '加载中...' : '未发现任何子代理' 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 = () => { const renderExpertModal = () => {
if (!editingExpert) return null if (!editingExpert) return null
const isEdit = editingExpert.mode === 'edit' const isEdit = editingExpert.mode === 'edit'