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:
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user