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

如何在React MUI TreeView中使用自定义可拖拽组件?

解决方案:TreeView拖拽项与放置目标组件复用

核心思路

无需重写TreeItem的内置功能,通过组合而非替换的方式,让自定义组件同时适配TreeView和放置目标场景,既保留TreeItem的展开/折叠/选择功能,又统一UI样式。

实现步骤

1. 提取通用UI组件

先把带彩色图标和文字的项框抽成独立组件,作为所有场景的基础UI:

// SharedItemUI.jsx
import { Box, Typography, Icon } from '@mui/material';

const SharedItemUI = ({ itemData, isDragging = false }) => {
  return (
    <Box
      sx={{
        display: 'flex',
        alignItems: 'center',
        padding: '8px 12px',
        borderRadius: '4px',
        backgroundColor: isDragging ? '#f0f0f0' : '#ffffff',
        border: '1px solid #e0e0e0',
        opacity: isDragging ? 0.7 : 1,
        cursor: 'grab'
      }}
    >
      <Icon sx={{ color: itemData.iconColor, marginRight: '8px' }}>
        {itemData.icon}
      </Icon>
      <Typography variant="body2">{itemData.label}</Typography>
    </Box>
  );
};

export default SharedItemUI;

2. 封装可拖拽TreeItem组件

基于MUI原生TreeItem,把SharedItemUI作为ContentComponent传入,同时集成React DnD的拖拽逻辑,完全保留TreeItem的内置功能:

// DraggableTreeItem.jsx
import { TreeItem, TreeItemProps } from '@mui/lab';
import { useDrag } from 'react-dnd';
import SharedItemUI from './SharedItemUI';
import { MyDnDItemType } from './DnDTypes';

const DraggableTreeItem = (props: TreeItemProps & { itemData: any }) => {
  const [{ isDragging }, drag] = useDrag({
    collect: (monitor) => ({
      isDragging: monitor.isDragging()
    }),
    item: { ...props.itemData, type: MyDnDItemType },
    type: MyDnDItemType
  });

  return (
    <TreeItem
      ref={drag}
      {...props}
      // 用SharedItemUI替换默认内容组件
      ContentComponent={() => <SharedItemUI itemData={props.itemData} isDragging={isDragging} />}
      // 隐藏TreeItem默认图标,用SharedItemUI里的自定义图标
      icon={null}
    />
  );
};

export default DraggableTreeItem;

3. 放置目标中复用SharedItemUI

在右侧放置目标里,直接使用SharedItemUI来渲染已拖拽进来的项,保证样式完全一致:

// DropTargetContainer.jsx
import { Box } from '@mui/material';
import { useDrop } from 'react-dnd';
import SharedItemUI from './SharedItemUI';
import { MyDnDItemType } from './DnDTypes';

const DropTargetContainer = ({ items, onAddItem }) => {
  const [{ isOver }, drop] = useDrop({
    accept: MyDnDItemType,
    drop: (item) => onAddItem(item),
    collect: (monitor) => ({
      isOver: monitor.isOver()
    })
  });

  return (
    <Box
      ref={drop}
      sx={{
        padding: '16px',
        border: `2px dashed ${isOver ? '#1976d2' : '#e0e0e0'}`,
        minHeight: '200px',
        borderRadius: '8px'
      }}
    >
      {items.map((item, index) => (
        <SharedItemUI key={index} itemData={item} />
      ))}
    </Box>
  );
};

export default DropTargetContainer;

关键说明

  • 用ContentComponent替换TreeItem的内容,无需重写展开/折叠/选择逻辑,MUI原生TreeItem的所有功能都会保留
  • SharedItemUI作为纯展示组件,只负责样式和渲染,拖拽逻辑分别在DraggableTreeItem和DropTargetContainer中处理
  • 通过isDragging状态统一控制拖拽时的UI变化(透明度、背景色),保持交互一致性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 06:36:31