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

使用React与react-dnd递归构建拖拽组件遇阻求助

React-DnD递归组件拖拽问题:仅最后一个子组件绑定拖拽的解决方案

我之前也碰到过类似的react-dnd递归拖拽的坑,你的情况大概率是递归渲染时没给每个子组件正确绑定拖拽能力,再加上原代码里的JSX语法错误,才导致只有最后一个子组件的拖拽功能生效,其余都是draggable:"false"。咱们一步步来拆解解决:

先看原代码里的核心问题

  1. JSX语法混乱:render方法里的返回内容格式不对,没有用合法的容器元素包裹,而且map循环里直接return空的(),根本没渲染子组件
  2. 递归渲染逻辑错误:没有把每个子节点包装成DragItem组件,导致只有最后一个组件能拿到拖拽的连接函数
  3. connectDragSource使用不当:没有正确用它包裹当前组件的根元素,拖拽事件无法绑定到组件上

修正后的完整代码(Class组件版本)

import { DragSource } from 'react-dnd';

// 定义拖拽类型常量
const Types = { ASSET: 'ASSET' };

// 拖拽源配置
const assetSource = {
  beginDrag(props) {
    // 拖拽开始时传递的数据,这里传递当前组件的name
    return { name: props.name };
  },
};

@DragSource(Types.ASSET, assetSource, (connect, monitor) => ({
  connectDragSource: connect.dragSource(),
}))
class DragItem extends React.Component {
  render() {
    const { connectDragSource, name, children } = this.props;
    
    // 用connectDragSource包裹整个组件的根元素,确保拖拽事件绑定到整个组件
    return connectDragSource(
      <div className="drag-item">
        {/* 渲染当前组件的名称 */}
        <span>{name}</span>
        {/* 递归渲染子组件:每个子节点都要实例化DragItem,确保每个子组件都有自己的拖拽能力 */}
        {children && children.map((childName, index) => (
          // 必须加key属性,避免React复用组件导致状态混乱
          <DragItem key={index} name={childName} />
        ))}
      </div>
    );
  }
}

关键修复点说明

  • 每个子组件都要被DragSource装饰:递归渲染时,一定要把每个子节点都创建成DragItem实例,这样每个组件都会获得独立的connectDragSource连接,而不是只有最后一个生效
  • 修复JSX结构:用合法的容器(比如div)包裹内容,map循环里返回正确的子组件实例,同时加上唯一的key(实际项目里最好用业务唯一ID,不要用index)
  • 正确绑定拖拽事件:必须用connectDragSource包裹当前组件的根元素,这样整个组件区域都能触发拖拽操作
  • 明确拖拽数据传递:在assetSource的beginDrag方法里明确传递当前组件的标识数据,方便后续拖拽目标识别

推荐:用函数组件+useDrag钩子实现(更简洁不易出错)

如果你的项目支持函数组件,更推荐用react-dnd的useDrag钩子,写法更直观,也能避免class组件的一些绑定问题:

import { useDrag } from 'react-dnd';

const Types = { ASSET: 'ASSET' };

function DragItem({ name, children }) {
  // 使用useDrag钩子获取拖拽状态和连接函数
  const [{ isDragging }, connectDragSource] = useDrag(() => ({
    type: Types.ASSET,
    item: { name },
    collect: (monitor) => ({
      isDragging: monitor.isDragging(), // 可以根据拖拽状态添加样式
    }),
  }));

  return connectDragSource(
    <div className={`drag-item ${isDragging ? 'opacity-50' : ''}`}>
      <span>{name}</span>
      {children && children.map((childName, index) => (
        <DragItem key={index} name={childName} />
      ))}
    </div>
  );
}

额外排查点

  • 确保你的整个应用已经被DndProvider包裹(react-dnd v14+需要显式导入包裹),否则拖拽上下文无法传递
  • 检查每个DragItem的props是否唯一,避免React因为props重复而复用组件实例,导致拖拽状态异常

内容的提问来源于stack exchange,提问作者Ravalika G

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:17:43