fix: prevent multiple initialization and state race condition in PopoutPage

- Use useRef to prevent multiple initializations
- Remove selectFile call that may cause state race condition
- Directly update store without selectFile to preserve isEditing state
This commit is contained in:
2026-03-22 00:16:22 +08:00
parent 3b1f99951e
commit 69bd91797d

View File

@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react' import { useEffect, useState, useRef } from 'react'
import { useSearchParams } from 'react-router-dom' import { useSearchParams } from 'react-router-dom'
import type { FileItemDTO } from '@shared/types/file' import type { FileItemDTO } from '@shared/types/file'
import { matchModule } from '@/lib/module-registry' import { matchModule } from '@/lib/module-registry'
@@ -11,15 +11,14 @@ import { useTabStore } from '@/stores'
export const PopoutPage = () => { export const PopoutPage = () => {
const [searchParams] = useSearchParams() const [searchParams] = useSearchParams()
const [file, setFile] = useState<FileItemDTO | null>(null) const [file, setFile] = useState<FileItemDTO | null>(null)
const [content, setContent] = useState<string>('')
const [unsavedContent, setUnsavedContent] = useState<string>('')
const [isEditing, setIsEditing] = useState(false)
const [isMaximized, setIsMaximized] = useState(false) const [isMaximized, setIsMaximized] = useState(false)
const [ready, setReady] = useState(false) const [ready, setReady] = useState(false)
const initializedRef = useRef(false)
const { selectFile } = useTabStore()
useEffect(() => { useEffect(() => {
if (initializedRef.current) return
initializedRef.current = true
const path = searchParams.get('path') const path = searchParams.get('path')
const name = searchParams.get('name') const name = searchParams.get('name')
const contentParam = searchParams.get('content') const contentParam = searchParams.get('content')
@@ -36,55 +35,61 @@ export const PopoutPage = () => {
const decodedUnsaved = unsavedParam ? decodeURIComponent(unsavedParam) : decodedContent const decodedUnsaved = unsavedParam ? decodeURIComponent(unsavedParam) : decodedContent
const decodedEditing = editingParam === 'true' const decodedEditing = editingParam === 'true'
setFile({ const fileObj: FileItemDTO = {
name: decodedName, name: decodedName,
path: decodedPath, path: decodedPath,
type: 'file', type: 'file',
size: 0, size: 0,
modified: new Date().toISOString(), modified: new Date().toISOString(),
}) }
setContent(decodedContent)
setUnsavedContent(decodedUnsaved)
setIsEditing(decodedEditing)
useTabStore.setState((state) => { setFile(fileObj)
const newTabs = new Map(state.tabs)
newTabs.set(decodedPath, { const existingTab = useTabStore.getState().tabs.get(decodedPath)
file: { if (!existingTab) {
name: decodedName, useTabStore.setState((state) => {
path: decodedPath, const newTabs = new Map(state.tabs)
type: 'file' as const, newTabs.set(decodedPath, {
size: 0, file: fileObj,
modified: new Date().toISOString(), content: decodedContent,
}, unsavedContent: decodedUnsaved,
content: decodedContent, isEditing: decodedEditing,
unsavedContent: decodedUnsaved, loading: false,
isEditing: decodedEditing, loaded: true,
loading: false, })
loaded: true, return {
tabs: newTabs,
activeTabId: decodedPath,
}
}) })
return { } else {
tabs: newTabs, useTabStore.setState((state) => {
activeTabId: decodedPath, const newTabs = new Map(state.tabs)
} const existing = newTabs.get(decodedPath)
}) if (existing) {
newTabs.set(decodedPath, {
selectFile({ ...existing,
name: decodedName, content: decodedContent,
path: decodedPath, unsavedContent: decodedUnsaved,
type: 'file', isEditing: decodedEditing,
size: 0, loaded: true,
modified: new Date().toISOString(), })
}) }
return {
setReady(true) tabs: newTabs,
activeTabId: decodedPath,
}
})
}
window.electronAPI?.windowIsMaximized().then((result) => { window.electronAPI?.windowIsMaximized().then((result) => {
if (result.success) { if (result.success) {
setIsMaximized(result.isMaximized) setIsMaximized(result.isMaximized)
} }
}) })
}, [searchParams, selectFile])
setReady(true)
}, [searchParams])
const handleMinimize = () => { const handleMinimize = () => {
window.electronAPI?.windowMinimize() window.electronAPI?.windowMinimize()