React动态组件卸载时无法移除事件监听器报错问题咨询
问题根因
你遇到的报错存在两个核心诱因:
- 事件处理函数引用不匹配:
addEventListener和removeEventListener传入的是不同的匿名箭头函数,二者逻辑相同但属于独立的函数引用,浏览器无法识别为需要移除的监听函数,即使没有DOM缺失的问题也无法完成解绑。 - 清理阶段DOM查询失败:useEffect的清理函数执行在组件卸载完成之后,此时组件关联的DOM节点已经从页面移除,
document.getElementById会返回null,调用null的removeEventListener方法就会触发类型错误。
修复后的代码
useEffect(() => { // 组件挂载时提前获取DOM并存储到当前作用域,清理函数可直接复用引用 const playerEl = document.getElementById('player'); const fullscreenBtn = document.getElementById('fullscreen'); const exitFullscreenBtn = document.getElementById('fullscreen-out'); // 抽离具名事件处理函数,保证绑定和解绑使用相同引用 const enterFullscreenHandler = () => { if (screenfull.isEnabled) { screenfull.request(playerEl); } }; const exitFullscreenHandler = () => { if (screenfull.isEnabled) { screenfull.toggle(playerEl); } }; // 绑定前先判空,避免元素未渲染导致的报错 fullscreenBtn?.addEventListener('click', enterFullscreenHandler); exitFullscreenBtn?.addEventListener('click', exitFullscreenHandler); return () => { // 清理时直接使用提前存储的引用,判空后再执行解绑 fullscreenBtn?.removeEventListener('click', enterFullscreenHandler); exitFullscreenBtn?.removeEventListener('click', exitFullscreenHandler); } }, [])
最佳实践优化
更推荐使用React的ref机制获取DOM元素,避免全局DOM查询可能带来的元素匹配冲突问题,稳定性更高:
// 组件内先声明对应DOM的ref const playerRef = useRef(null); const fullscreenBtnRef = useRef(null); const exitFullscreenBtnRef = useRef(null); useEffect(() => { const playerEl = playerRef.current; const fullscreenBtn = fullscreenBtnRef.current; const exitFullscreenBtn = exitFullscreenBtnRef.current; const enterFullscreenHandler = () => { if (screenfull.isEnabled) { screenfull.request(playerEl); } }; const exitFullscreenHandler = () => { if (screenfull.isEnabled) { screenfull.toggle(playerEl); } }; fullscreenBtn?.addEventListener('click', enterFullscreenHandler); exitFullscreenBtn?.addEventListener('click', exitFullscreenHandler); return () => { fullscreenBtn?.removeEventListener('click', enterFullscreenHandler); exitFullscreenBtn?.removeEventListener('click', exitFullscreenHandler); } }, []) // 渲染时给对应元素绑定ref return ( <div> <div ref={playerRef} id="player">播放器容器</div> <button ref={fullscreenBtnRef} id="fullscreen">进入全屏</button> <button ref={exitFullscreenBtnRef} id="fullscreen-out">退出全屏</button> </div> )
内容的提问来源于stack exchange,提问作者jACK
相关产品推荐
相关产品推荐

