如何在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
相关产品推荐
相关产品推荐

