react 实现拖拽
实现拖拽的基本步骤
安装依赖库react-dnd和react-dnd-html5-backend:
npm install react-dnd react-dnd-html5-backend
引入必要模块:
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
创建可拖拽组件
定义拖拽源组件:
const DraggableItem = ({ id, text }) => {
const [{ isDragging }, drag] = useDrag(() => ({
type: 'ITEM',
item: { id },
collect: (monitor) => ({
isDragging: !!monitor.isDragging(),
}),
}));
return (
<div
ref={drag}
style={{
opacity: isDragging ? 0.5 : 1,
padding: '8px',
margin: '4px',
backgroundColor: '#f0f0f0',
cursor: 'move',
}}
>
{text}
</div>
);
};
创建放置目标组件
定义放置目标组件:
const DropZone = ({ onDrop }) => {
const [{ canDrop, isOver }, drop] = useDrop(() => ({
accept: 'ITEM',
drop: (item) => onDrop(item.id),
collect: (monitor) => ({
isOver: !!monitor.isOver(),
canDrop: !!monitor.canDrop(),
}),
}));
const backgroundColor = canDrop ? (isOver ? '#d4edda' : '#f8f9fa') : '#ffffff';
return (
<div
ref={drop}
style={{
padding: '16px',
margin: '8px',
border: '2px dashed #ccc',
backgroundColor,
}}
>
{canDrop ? '释放放置' : '拖拽到此区域'}
</div>
);
};
组合组件实现完整功能
实现完整的拖拽交互:
const App = () => {
const [items, setItems] = useState([
{ id: 1, text: '项目1' },
{ id: 2, text: '项目2' },
{ id: 3, text: '项目3' },
]);
const [droppedItems, setDroppedItems] = useState([]);
const handleDrop = (id) => {
const item = items.find((i) => i.id === id);
setDroppedItems([...droppedItems, item]);
setItems(items.filter((i) => i.id !== id));
};
return (
<DndProvider backend={HTML5Backend}>
<div style={{ display: 'flex' }}>
<div style={{ width: '200px' }}>
{items.map((item) => (
<DraggableItem key={item.id} id={item.id} text={item.text} />
))}
</div>
<DropZone onDrop={handleDrop} />
<div>
{droppedItems.map((item) => (
<div key={item.id}>{item.text}</div>
))}
</div>
</div>
</DndProvider>
);
};
自定义拖拽预览
实现自定义拖拽预览效果:
const CustomDragPreview = ({ item }) => {
return (
<div
style={{
backgroundColor: '#007bff',
color: 'white',
padding: '8px',
borderRadius: '4px',
boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
}}
>
{item.text}
</div>
);
};
// 在useDrag配置中添加preview选项
const [{ isDragging }, drag, preview] = useDrag(() => ({
type: 'ITEM',
item: { id, text },
}));
return (
<>
<div ref={preview} style={{ display: 'none' }} />
<div ref={drag}>{text}</div>
</>
);
处理复杂数据类型
处理复杂数据类型的拖拽:
const handleDrop = (item) => {
if (item.type === 'TASK') {
// 处理任务数据
} else if (item.type === 'FILE') {
// 处理文件数据
}
};
// 在useDrop中配置多种accept类型
useDrop(() => ({
accept: ['TASK', 'FILE'],
drop: handleDrop,
}));
实现排序功能
实现列表内拖拽排序:
const SortableList = ({ items, onMove }) => {
const moveItem = (dragIndex, hoverIndex) => {
const dragItem = items[dragIndex];
const newItems = [...items];
newItems.splice(dragIndex, 1);
newItems.splice(hoverIndex, 0, dragItem);
onMove(newItems);
};
return (
<div>
{items.map((item, index) => (
<SortableItem
key={item.id}
index={index}
item={item}
moveItem={moveItem}
/>
))}
</div>
);
};
const SortableItem = ({ index, item, moveItem }) => {
const ref = useRef(null);
const [{ isDragging }, drag] = useDrag({
type: 'ITEM',
item: { index },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
});
const [, drop] = useDrop({
accept: 'ITEM',
hover: (draggedItem, monitor) => {
if (draggedItem.index === index) return;
moveItem(draggedItem.index, index);
draggedItem.index = index;
},
});
drag(drop(ref));
return (
<div
ref={ref}
style={{
opacity: isDragging ? 0.5 : 1,
padding: '8px',
margin: '4px',
backgroundColor: '#f0f0f0',
cursor: 'move',
}}
>
{item.text}
</div>
);
};






