You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React实现Drawer导航拖拽缩放异常:首次生效后无法重复绑定事件

解决React Drawer拖拽调宽时mouseup监听器无法重复绑定的问题

哎,这个场景我太熟了!首次拖拽没问题,之后再点Dragger就没反应,本质是监听器的绑定和清理没做好,加上React函数组件的渲染特性搞的鬼。我给你拆解下问题和解决方案:

问题根源

  1. 监听器没清理:第一次mouseup触发后,你没把绑定在document上的handleMouseUp移除,后续再触发mousedown会重复绑定多个监听器,而且这些监听器的函数引用还可能因为组件重渲染(比如抽屉宽度更新导致重渲染)而失效。
  2. 函数引用不稳定: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:30:52