用react实现拖拽
实现拖拽功能的基本步骤
安装依赖库react-dnd
npm install react-dnd react-dnd-html5-backend
创建可拖拽组件
import { useDrag } from 'react-dnd';
function 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,
cursor: 'move',
padding: '8px',
margin: '4px',
backgroundColor: 'lightblue',
}}
>
{text}
</div>
);
}
创建放置区域组件
import { useDrop } from 'react-dnd';
function DropZone({ onDrop }) {
const [{ isOver }, drop] = useDrop(() => ({
accept: 'ITEM',
drop: (item) => onDrop(item.id),
collect: (monitor) => ({
isOver: !!monitor.isOver(),
}),
}));
return (
<div
ref={drop}
style={{
minHeight: '200px',
border: isOver ? '2px dashed red' : '2px dashed gray',
padding: '16px',
margin: '16px',
}}
>
放置区域
</div>
);
}
组合组件实现完整功能
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
function App() {
const [items, setItems] = useState([
{ id: 1, text: '项目1' },
{ id: 2, text: '项目2' },
{ id: 3, text: '项目3' },
]);
const handleDrop = (id) => {
console.log(`项目${id}被放置`);
};
return (
<DndProvider backend={HTML5Backend}>
<div>
{items.map((item) => (
<DraggableItem key={item.id} id={item.id} text={item.text} />
))}
<DropZone onDrop={handleDrop} />
</div>
</DndProvider>
);
}
自定义拖拽手柄实现
仅允许特定元素触发拖拽

function DraggableWithHandle({ id, text }) {
const [{ isDragging }, drag, dragPreview] = useDrag(() => ({
type: 'ITEM',
item: { id },
}));
return (
<div ref={dragPreview} style={{ opacity: isDragging ? 0.5 : 1 }}>
<div ref={drag} style={{ cursor: 'move', padding: '4px' }}>
🖐️ 拖拽手柄
</div>
<div style={{ padding: '8px', backgroundColor: 'lightgreen' }}>
{text}
</div>
</div>
);
}
拖拽排序实现
列表项拖拽排序逻辑
const moveItem = (dragIndex, hoverIndex) => {
setItems((prevItems) => {
const newItems = [...prevItems];
const [removed] = newItems.splice(dragIndex, 1);
newItems.splice(hoverIndex, 0, removed);
return newItems;
});
};
// 在DropZone组件中使用hover回调
const [, drop] = useDrop({
accept: 'ITEM',
hover(item, monitor) {
if (!ref.current) return;
const dragIndex = item.index;
const hoverIndex = index;
if (dragIndex === hoverIndex) return;
moveItem(dragIndex, hoverIndex);
item.index = hoverIndex;
},
});
触摸设备支持
安装触摸后端
npm install react-dnd-touch-backend
配置触摸后端

import { TouchBackend } from 'react-dnd-touch-backend';
// 在应用中使用
<DndProvider backend={TouchBackend} options={{ enableMouseEvents: true }}>
{/* 应用内容 */}
</DndProvider>
性能优化建议
使用memo避免不必要的渲染
const DraggableItem = React.memo(function DraggableItem({ id, text }) {
// 组件实现
});
减少收集函数复杂度
const [, drop] = useDrop(() => ({
accept: 'ITEM',
drop: (item) => ({ id: item.id }),
// 简单的collect函数
collect: (monitor) => ({
isOver: monitor.isOver(),
canDrop: monitor.canDrop(),
}),
}));
高级用法示例
嵌套拖拽容器
function NestedContainer() {
const [containers, setContainers] = useState([
{ id: 'container1', items: [...] },
{ id: 'container2', items: [...] },
]);
const moveItemBetweenContainers = (itemId, fromContainerId, toContainerId) => {
// 实现跨容器移动逻辑
};
return containers.map((container) => (
<div key={container.id}>
<h3>容器 {container.id}</h3>
<DropZone
containerId={container.id}
onDrop={(item) => moveItemBetweenContainers(item.id, item.fromContainer, container.id)}
>
{container.items.map((item) => (
<DraggableItem
key={item.id}
{...item}
fromContainer={container.id}
/>
))}
</DropZone>
</div>
));
}
拖拽预览自定义
const [, drag, preview] = useDrag(() => ({
type: 'ITEM',
item: { id },
}));
useEffect(() => {
preview(getEmptyImage(), { captureDraggingState: true });
}, []);
// 自定义预览组件
<div ref={preview} style={{ position: 'absolute', opacity: 0 }} />






