使用D3与React实现拖拽时图形重复问题排查
问题分析与解决方案
为什么会出现两个矩形?
这是**React开发环境的严格模式(Strict Mode)**导致的。开发模式下,Strict Mode会刻意重复调用副作用函数(比如useEffect的回调),用于检测潜在代码问题。你的useEffect每次执行都会调用svg.append('rect'),且没有清理逻辑,两次执行就生成了两个独立的矩形。
拖拽时原位置留图形的本质
并非原位置图形未移动,而是两个矩形初始时重叠在一起,拖拽其中一个后,另一个留在初始位置,视觉上看起来像原图形没动。
用useState出现拖尾的原因
使用useState管理位置时,每次状态更新都会触发组件重新渲染。如果没有正确清理之前用D3创建的旧元素,每次渲染都会新增一个矩形,多个矩形跟随鼠标移动就会形成拖尾效果。
修复步骤
1. 给useEffect添加清理逻辑,避免重复创建元素
修改Comp.jsx中的useEffect,添加清理函数,在组件卸载或useEffect重新执行时移除之前创建的矩形:
useEffect(() => { const svg = d3.select(svgRef.current); // 创建矩形 const rect = svg.append('rect') .attr('x', 50) .attr('y', 50) .attr('width', 50) .attr('height', 50) .attr('fill', 'blue'); // 修正拖拽逻辑:记录偏移量,让拖拽更自然 let offsetX, offsetY; rect.call( d3.drag() .on('start', function(event) { // 记录鼠标与矩形左上角的偏移距离 const rectNode = d3.select(this); offsetX = event.x - parseFloat(rectNode.attr('x')); offsetY = event.y - parseFloat(rectNode.attr('y')); }) .on('drag', function(event) { // 用鼠标位置减去偏移量,得到矩形新的左上角坐标 d3.select(this) .attr('x', event.x - offsetX) .attr('y', event.y - offsetY); }) ); // 清理函数:移除当前创建的矩形 return () => { rect.remove(); }; }, []);
2. 优化拖拽位置计算(体验提升)
原拖拽逻辑直接将鼠标位置赋值给矩形的x/y,会导致矩形左上角瞬间跳到鼠标位置,体验生硬。上述代码新增了start事件记录偏移量,拖拽时用鼠标位置减去偏移量,让拖拽行为更符合直觉——点击矩形任意位置,都能从该点开始拖动。
3. 临时关闭Strict Mode(仅作测试)
若要快速验证Strict Mode的影响,可暂时移除index.js中的<React.StrictMode>标签,但不推荐作为最终方案,因为Strict Mode能帮你提前发现潜在代码问题。
内容的提问来源于stack exchange,提问作者user22133780
相关产品推荐
相关产品推荐

