Skip to content

fix(ui): unlisten Tauri events when unmounted before listen() resolves#730

Open
Felix201209 wants to merge 1 commit into
betafrom
fix/tauri-listen-unmount-leak
Open

fix(ui): unlisten Tauri events when unmounted before listen() resolves#730
Felix201209 wants to merge 1 commit into
betafrom
fix/tauri-listen-unmount-leak

Conversation

@Felix201209

@Felix201209 Felix201209 commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

User description

摘要

修复 3 处 useEffect 里 Tauri 事件监听的清理竞态:组件在异步 listen() 的 promise resolve 之前就卸载时,会泄漏一个永不解绑的监听器。

问题

这几处用的是:

}).then(fn => { if (!cancelled) unlisten = fn; }).catch(() => {});

如果组件在 listen() resolve 前就卸载,cancelled 已经是 true,于是 unlisten 永远不会被赋值——但底层 Tauri 订阅其实已经创建。cleanup 里的 unlisten?.() 是空操作,监听器就此泄漏,并继续在已卸载的组件上触发回调(setMaximized / setState 等),造成无谓开销和 React 警告。

WindowChromeLinuxTitlebartauri://resize)最容易复现:在那个动态 import(...) + w.listen() 还在飞行途中时快速重挂载窗口 chrome,就会泄漏一个 resize 监听器。

改动

对齐到仓库自己已有的正确约定Capsule.tsx / Vocab.tsx / LessComputerPanel.tsx / LessComputerGlow.tsx):

}).then(fn => { if (cancelled) fn(); else unlisten = fn; }).catch(() => {});

已卸载就立即解绑。涉及:

  • components/AudioCue.tsxprefs:changedcapsule:state 两个监听
  • components/WindowChrome.tsxLinuxTitlebartauri://resize 监听

共 +4/-3,纯前端、行为对齐既有约定、低风险。

验证

  • tsc --noEmit 通过。

🤖 Generated with Claude Code


PR Type

Bug fix


Description

  • Prevent Tauri event listener leaks on unmounted components

  • Fix race condition: unlisten handle dropped if component unmounts before async listen() resolves

  • Align AudioCue.tsx and WindowChrome.tsx with established cleanup pattern

  • Low-risk, pure front-end changes


Diagram Walkthrough

flowchart LR
    A["Component mounts"] --> B["listen() async"]
    B --> C{"Unmounted before resolve?"}
    C -- Yes --> D["Call unlisten fn immediately"]
    C -- No --> E["Store unlisten handle"]
    D --> F["Cleanup on unmount"]
    E --> F
Loading

File Walkthrough

Relevant files
Bug fix
AudioCue.tsx
Fix Tauri listener cleanup in AudioCue                                     

openless-all/app/src/components/AudioCue.tsx

  • Fixed two Tauri event listeners (prefs:changed and capsule:state) to
    immediately unsubscribe if component unmounted before listen()
    resolves.
  • Changed from if (!cancelled) unlisten = fn; to if (cancelled) fn();
    else unlisten = fn;.
+2/-2     
WindowChrome.tsx
Fix Tauri listener cleanup in LinuxTitlebar                           

openless-all/app/src/components/WindowChrome.tsx

  • Fixed tauri://resize listener in LinuxTitlebar to unsubscribe
    immediately if component unmounts before listen() resolves.
  • Changed from if (!cancelled) unlisten = fn; to if (cancelled) fn();
    else unlisten = fn;.
+2/-1     

Three useEffect cleanups stored the unlisten handle with
`if (!cancelled) unlisten = fn`. When the component unmounts before the
async `listen()` promise resolves, `cancelled` is already true, so the
handle is dropped on the floor — the underlying Tauri subscription was
created but is never torn down. The leaked listener keeps firing (and
calling setState) on an unmounted component.

Align these three sites with the repo's own established convention
(Capsule.tsx / Vocab.tsx / LessComputerPanel.tsx / LessComputerGlow.tsx):
`if (cancelled) fn(); else unlisten = fn;` so an already-unmounted effect
immediately unsubscribes.

- AudioCue.tsx: `prefs:changed` and `capsule:state` listeners
- WindowChrome.tsx: LinuxTitlebar `tauri://resize` listener

Verified: `tsc --noEmit` passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 21, 2026 15:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 1 🔵⚪⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ No major issues detected

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants