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

React DnD嵌套组件:子组件可设props但父组件拖放事件失效

解决React DnD中父DropTarget无法更新数据的问题

首先得明确一个React核心原则:组件的props是只读的,不能直接修改。你提到父组件在drop事件里“无法设置props”,大概率是因为你在尝试直接修改父组件的props——这本身就不符合React的设计逻辑,而子组件能生效,可能是因为子组件在操作自己的内部state,而非props。

结合你的代码结构,我来一步步帮你解决这个问题:

1. 先梳理你的代码矛盾点

你的结构是ComponentA作为父容器,内部渲染多个ComponentB,两者都是DropTarget,且共用相同的drop逻辑。但你给ComponentA传的是handleRootDropEvent,给ComponentB传的是handleDropEvent——如果这两个函数逻辑一致,建议统一成一个,避免重复代码,也能减少作用域问题。

2. 核心解决方案:统一状态管理

所有分类数据(this.state.categories)应该由最上层的父组件(也就是渲染ComponentA的那个组件)来维护,不管是ComponentA还是ComponentB触发drop事件,都通知这个最上层组件去更新state,再通过props把新数据传递给所有子组件。这样既能避免直接修改props的问题,也能保证数据一致性。

步骤1:把drop逻辑统一到最上层父组件

// 最上层的父组件,也就是你当前持有categories state的组件
class CategoryContainer extends React.Component {
  state = {
    categories: [] // 你的分类数据
  };

  // 统一处理所有drop事件的函数
  handleDrop = (dropResult, sourceProps) => {
    // 根据触发drop的组件类型(父/子)和拖拽数据,更新分类
    const updatedCategories = this.updateCategoryStructure(
      this.state.categories,
      dropResult,
      sourceProps
    );
    // 用setState更新状态,React会自动把新数据传递给子组件
    this.setState({ categories: updatedCategories });
  };

  // 具体的分类更新逻辑,根据你的业务需求调整
  updateCategoryStructure = (categories, dropResult, sourceProps) => {
    const draggedItem = dropResult;
    // 区分是ComponentA(根容器)还是ComponentB(子分类)触发的drop
    if (sourceProps.componentType === 'root') {
      // 拖拽到根容器:把分类添加到根级别
      return [...categories, { ...draggedItem, level: 0, parentCat: null }];
    } else {
      // 拖拽到子分类:把分类添加到当前分类下
      return categories.map(cat => {
        if (cat.id === sourceProps.categoryId) {
          return {
            ...cat,
            children: [...(cat.children || []), { ...draggedItem, level: sourceProps.level + 1, parentCat: cat.id }]
          };
        }
        return cat;
      });
    }
  };

  render() {
    return (
      <ComponentA 
        dropEvent={this.handleDrop} 
        componentType="root" // 标记是根容器组件
      >
        <p>Displayed Categories </p>
        {this.state.categories.map((c, idx) => 
          <ComponentB 
            key={idx} 
            parentCat={null} 
            thisCat={c} 
            level={c.level} 
            dropEvent={this.handleDrop} 
            componentType="child" // 标记是子分类组件
            categoryId={c.id} // 传递当前分类ID,方便更新时定位
          /> 
        )}
      </ComponentA>
    );
  }
}

步骤2:在DropTarget组件中正确传递事件参数

不管是ComponentA还是ComponentB,作为DropTarget,它们的drop函数需要把拖拽结果和自身的props传递给上层的handleDrop函数,这样上层组件就能知道是哪个组件触发的drop,从而做出正确的更新:

// ComponentA的DropTarget封装
import { DropTarget } from 'react-dnd';

const ComponentADropTarget = DropTarget(
  'CATEGORY', // 拖拽类型,要和拖拽源的类型一致
  {
    drop(props, monitor) {
      const dropResult = monitor.getItem(); // 获取拖拽的内容
      // 调用上层传递的dropEvent,把拖拽结果和自身props传过去
      props.dropEvent(dropResult, props);
    }
  },
  (connect) => ({
    connectDropTarget: connect.dropTarget()
  })
)(({ connectDropTarget, children, ...props }) => {
  return connectDropTarget(
    <div {...props}>
      {children}
    </div>
  );
});

// ComponentB的DropTarget封装(逻辑和ComponentA一致)
const ComponentBDropTarget = DropTarget(
  'CATEGORY',
  {
    drop(props, monitor) {
      const dropResult = monitor.getItem();
      props.dropEvent(dropResult, props);
    }
  },
  (connect) => ({
    connectDropTarget: connect.dropTarget()
  })
)(({ connectDropTarget, thisCat, ...props }) => {
  return connectDropTarget(
    <div {...props}>
      {/* 这里渲染你的子分类内容 */}
      {thisCat.name}
    </div>
  );
});

3. 为什么之前父组件无法“设置props”?

你之前的问题本质是:

  • 父组件(ComponentA)的drop函数里尝试直接修改自身的props,但React中props是只读的,无法直接赋值修改
  • 子组件(ComponentB)可能是在修改自己的内部state,所以能看到局部效果,但这种方式会导致状态分散,不同组件的数据无法同步

通过把状态统一维护在最上层父组件,所有drop操作都触发父组件的setState,就能解决这个问题,同时保证所有组件的数据都是同步的。


内容的提问来源于stack exchange,提问作者Daniel Thompson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 04:12:41