React实现Drawer导航拖拽缩放异常:首次生效后无法重复绑定事件
解决React Drawer拖拽调宽时mouseup监听器无法重复绑定的问题
哎,这个场景我太熟了!首次拖拽没问题,之后再点Dragger就没反应,本质是监听器的绑定和清理没做好,加上React函数组件的渲染特性搞的鬼。我给你拆解下问题和解决方案:
问题根源
- 监听器没清理:第一次
mouseup触发后,你没把绑定在document上的handleMouseUp移除,后续再触发mousedown会重复绑定多个监听器,而且这些监听器的函数引用还可能因为组件重渲染(比如抽屉宽度更新导致重渲染)而失效。 - 函数引用不稳定:React函数组件每次渲染都会重新创建
handleMouseUp和handleMouseDown,导致你绑定到document上的旧函数和当前组件里的新函数不是同一个引用,后续触发时根本跑不到你期望的逻辑。
修复后的代码方案
import { useCallback, useState } from 'react'; // 假设你的layout状态是用useState管理的 const [layout, setLayout] = useState({ drawerWidth: 300 }); const MIN_WIDTH = 57; const MAX_WIDTH = 600; // 用useCallback缓存函数,保证引用稳定 const handleMouseUp = useCallback((event: MouseEvent) => { // 先清理监听器,避免内存泄漏和重复绑定 document.removeEventListener('mouseup', handleMouseUp); const newWidth = event.clientX; if (newWidth > MIN_WIDTH && newWidth < MAX_WIDTH) { setLayout(prev => ({ ...prev, drawerWidth: newWidth })); } }, []); const handleMouseDown = useCallback((event: MouseEvent) => { // 绑定前先清理一次,防止之前的监听器残留 document.removeEventListener('mouseup', handleMouseUp); document.addEventListener('mouseup', handleMouseUp); }, [handleMouseUp]); // 额外优化:如果想要实时拖拽更新宽度(体验更好),可以加mousemove监听 const handleMouseMove = useCallback((event: MouseEvent) => { const newWidth = event.clientX; if (newWidth > MIN_WIDTH && newWidth < MAX_WIDTH) { setLayout(prev => ({ ...prev, drawerWidth: newWidth })); } }, []); const handleDragStart = useCallback((event: MouseEvent) => { // 清理残留的监听器 document.removeEventListener('mouseup', handleMouseUp); document.removeEventListener('mousemove', handleMouseMove); // 绑定实时拖拽和结束监听器 document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', () => { // 拖拽结束后清理所有监听器 document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }); }, [handleMouseUp, handleMouseMove]);
关键优化点
- 用useCallback缓存函数:保证
handleMouseUp、handleMouseMove的引用在组件生命周期内稳定,避免因为重渲染导致监听器绑定失效。 - 强制清理监听器:每次绑定前先移除一次相同的监听器,绑定后在
mouseup触发时必须清理,彻底避免重复绑定和内存泄漏。 - 实时拖拽优化:如果只在
mouseup时更新宽度,拖拽体验会很生硬,加上mousemove监听可以让宽度实时跟随鼠标移动,交互感更好。
另外,别忘了给你的Dragger元素加个样式,提升交互感知:
.dragger { cursor: col-resize; width: 4px; background-color: #eee; /* 可以再加个hover效果,让拖拽区更明显 */ transition: background-color 0.2s; } .dragger:hover { background-color: #ccc; }
内容的提问来源于stack exchange,提问作者Dito
相关产品推荐
相关产品推荐

