fix(folder): prevent stale file watch subscriptions in aux tabs

Ensure async watch setup is safely discarded after effect cleanup.

Use idempotent watcher release logic to avoid duplicate subscriptions and unbalanced stop calls.
This commit is contained in:
xintaofei
2026-04-14 16:43:35 +08:00
parent 9f1540129b
commit 90e8bb645a
2 changed files with 58 additions and 6 deletions

View File

@@ -615,8 +615,23 @@ export function GitChangesTab() {
if (!rootPath || !isChangesTabActive) return
let unlisten: (() => void) | null = null
let disposed = false
let watchStarted = false
let watchReleased = false
const normalizedRootPath = normalizeComparePath(rootPath)
const releaseWatch = () => {
if (watchReleased) return
watchReleased = true
if (unlisten) {
unlisten()
unlisten = null
}
if (watchStarted) {
void stopFileTreeWatch(rootPath)
}
}
const scheduleRefresh = () => {
if (refreshTimerRef.current) {
clearTimeout(refreshTimerRef.current)
@@ -629,12 +644,17 @@ export function GitChangesTab() {
const setup = async () => {
try {
await startFileTreeWatch(rootPath)
watchStarted = true
} catch {
// ignore watch startup errors
}
if (disposed) {
releaseWatch()
return
}
try {
unlisten = await subscribe<FileTreeChangedEvent>(
const subscribedUnlisten = await subscribe<FileTreeChangedEvent>(
"folder://file-tree-changed",
(payload) => {
if (
@@ -646,6 +666,12 @@ export function GitChangesTab() {
scheduleRefresh()
}
)
if (disposed) {
subscribedUnlisten()
releaseWatch()
return
}
unlisten = subscribedUnlisten
} catch {
// ignore listen errors
}
@@ -654,12 +680,12 @@ export function GitChangesTab() {
void setup()
return () => {
disposed = true
if (refreshTimerRef.current) {
clearTimeout(refreshTimerRef.current)
refreshTimerRef.current = null
}
unlisten?.()
void stopFileTreeWatch(rootPath)
releaseWatch()
}
}, [fetchChanges, folder?.path, isChangesTabActive])