在Tauri+React中如何正确在组件卸载时清理事件监听?
问题原因分析
React StrictMode 在开发环境会双重执行组件的挂载/卸载流程(先挂载→立即卸载→再次挂载),你的代码里registerListener是异步函数,第一次触发卸载时,unsubscribe还没完成赋值,导致第一次注册的监听完全没被清理;第二次挂载后又新增了一个监听,最终只有第二次的监听能被正常注销,残留的第一次监听就会出现「仅能完成一次注销」的异常。
修复方案
用useRef跟踪注销函数和组件挂载状态,确保异步流程中也能正确清理副作用:
import { useEffect, useRef } from 'react'; // ... useEffect(() => { const unsubscribeRef = useRef<(() => void) | null>(null); const isMounted = useRef(true); const registerListener = async () => { // 注册新监听前,先清理可能存在的旧监听 if (unsubscribeRef.current) { unsubscribeRef.current(); unsubscribeRef.current = null; } const unsubscribe = await listen<string>('new_number', (event) => { console.log('Received event:', event.payload); // 组件已卸载时,跳过状态更新 if (isMounted.current) { setNumber(event.payload); } }); // 组件仍挂载时才保存注销函数,否则直接注销 if (isMounted.current) { unsubscribeRef.current = unsubscribe; } else { unsubscribe(); } }; registerListener(); return () => { // 标记组件已卸载,阻断后续异步操作 isMounted.current = false; // 执行注销并清空ref if (unsubscribeRef.current) { unsubscribeRef.current(); unsubscribeRef.current = null; } }; }, []);
关键改动说明
- 用
unsubscribeRef存储注销函数:避免异步赋值时丢失引用,确保清理函数能拿到最新的注销方法。 - 增加
isMounted标志:防止组件卸载后还执行setNumber或赋值注销函数,避免内存泄漏。 - 注册前清理旧监听:应对StrictMode的双重挂载,避免重复注册多个监听。
内容的提问来源于stack exchange,提问作者Kelvin Lee
相关产品推荐
相关产品推荐

