fix(ui): unlisten Tauri events when unmounted before listen() resolves#730
Open
Felix201209 wants to merge 1 commit into
Open
fix(ui): unlisten Tauri events when unmounted before listen() resolves#730Felix201209 wants to merge 1 commit into
Felix201209 wants to merge 1 commit into
Conversation
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>
Contributor
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
User description
摘要
修复 3 处
useEffect里 Tauri 事件监听的清理竞态:组件在异步listen()的 promise resolve 之前就卸载时,会泄漏一个永不解绑的监听器。问题
这几处用的是:
如果组件在
listen()resolve 前就卸载,cancelled已经是true,于是unlisten永远不会被赋值——但底层 Tauri 订阅其实已经创建。cleanup 里的unlisten?.()是空操作,监听器就此泄漏,并继续在已卸载的组件上触发回调(setMaximized/setState等),造成无谓开销和 React 警告。WindowChrome的LinuxTitlebar(tauri://resize)最容易复现:在那个动态import(...)+w.listen()还在飞行途中时快速重挂载窗口 chrome,就会泄漏一个 resize 监听器。改动
对齐到仓库自己已有的正确约定(
Capsule.tsx/Vocab.tsx/LessComputerPanel.tsx/LessComputerGlow.tsx):已卸载就立即解绑。涉及:
components/AudioCue.tsx:prefs:changed与capsule:state两个监听components/WindowChrome.tsx:LinuxTitlebar的tauri://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 --> FFile Walkthrough
AudioCue.tsx
Fix Tauri listener cleanup in AudioCueopenless-all/app/src/components/AudioCue.tsx
prefs:changedandcapsule:state) toimmediately unsubscribe if component unmounted before
listen()resolves.
if (!cancelled) unlisten = fn;toif (cancelled) fn();else unlisten = fn;.WindowChrome.tsx
Fix Tauri listener cleanup in LinuxTitlebaropenless-all/app/src/components/WindowChrome.tsx
tauri://resizelistener inLinuxTitlebarto unsubscribeimmediately if component unmounts before
listen()resolves.if (!cancelled) unlisten = fn;toif (cancelled) fn();else unlisten = fn;.