()
| 6 | import './EditorArea.css' |
| 7 | |
| 8 | export function EditorArea(): React.JSX.Element { |
| 9 | const { tabs, activeTabId, cleanupInvalidTabs } = useTabsStore() |
| 10 | |
| 11 | // UI 层维护:记录哪些 tab 曾被激活过(用于懒加载 + 保持挂载) |
| 12 | const [activatedIds, setActivatedIds] = useState<Set<string>>(() => { |
| 13 | // 初始化时将当前 activeTabId 加入集合 |
| 14 | return activeTabId ? new Set([activeTabId]) : new Set() |
| 15 | }) |
| 16 | |
| 17 | // 使用 ref 存储 cleanupInvalidTabs 避免依赖变化 |
| 18 | const cleanupRef = useRef(cleanupInvalidTabs) |
| 19 | cleanupRef.current = cleanupInvalidTabs |
| 20 | |
| 21 | // 定期清理无效的 tabs(使用注册机制验证) |
| 22 | useEffect(() => { |
| 23 | cleanupRef.current() |
| 24 | }, [tabs.length]) |
| 25 | |
| 26 | // 当 activeTabId 变化时,将其加入已激活集合 |
| 27 | useEffect(() => { |
| 28 | if (activeTabId && !activatedIds.has(activeTabId)) { |
| 29 | setActivatedIds((prev) => new Set(prev).add(activeTabId)) |
| 30 | } |
| 31 | }, [activeTabId, activatedIds]) |
| 32 | |
| 33 | // 当 tab 被关闭时,从已激活集合中移除 |
| 34 | useEffect(() => { |
| 35 | const currentTabIds = new Set(tabs.map((t) => t.id)) |
| 36 | setActivatedIds((prev) => { |
| 37 | let changed = false |
| 38 | const next = new Set<string>() |
| 39 | for (const id of prev) { |
| 40 | if (currentTabIds.has(id)) { |
| 41 | next.add(id) |
| 42 | } else { |
| 43 | changed = true |
| 44 | } |
| 45 | } |
| 46 | return changed ? next : prev |
| 47 | }) |
| 48 | }, [tabs]) |
| 49 | |
| 50 | // 获取需要渲染的 tabs(已激活且仍存在) |
| 51 | const tabsToRender = tabs.filter((t) => activatedIds.has(t.id)) |
| 52 | |
| 53 | return ( |
| 54 | <div className="editor-area"> |
| 55 | <div className="editor-tabs"> |
| 56 | <Tabs /> |
| 57 | </div> |
| 58 | <div className="editor-content"> |
| 59 | {tabsToRender.length === 0 ? ( |
| 60 | <Empty description="打开一个聊天开始对话" style={{ marginTop: 100 }} /> |
| 61 | ) : ( |
| 62 | tabsToRender.map((tab) => ( |
| 63 | <div |
| 64 | key={tab.id} |
| 65 | className="editor-pane" |
nothing calls this directly
no test coverage detected