react实现table拖拽
实现React表格拖拽功能
使用react-dnd库
安装react-dnd及其HTML5后端依赖:
npm install react-dnd react-dnd-html5-backend
创建可拖拽的行组件:
import { useDrag, useDrop } from 'react-dnd';
const DraggableRow = ({ id, index, moveRow, children }) => {
const [{ isDragging }, dragRef] = useDrag({
type: 'ROW',
item: { id, index },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
});
const [, dropRef] = useDrop({
accept: 'ROW',
hover: (draggedItem) => {
if (draggedItem.index !== index) {
moveRow(draggedItem.index, index);
draggedItem.index = index;
}
},
});
return (
<tr
ref={(node) => dragRef(dropRef(node))}
style={{ opacity: isDragging ? 0.5 : 1 }}
>
{children}
</tr>
);
};
在表格组件中实现拖拽逻辑
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
const Table = ({ data, columns }) => {
const [items, setItems] = useState(data);
const moveRow = (fromIndex, toIndex) => {
const newItems = [...items];
const [movedItem] = newItems.splice(fromIndex, 1);
newItems.splice(toIndex, 0, movedItem);
setItems(newItems);
};
return (
<DndProvider backend={HTML5Backend}>
<table>
<thead>
<tr>
{columns.map((col) => (
<th key={col.key}>{col.title}</th>
))}
</tr>
</thead>
<tbody>
{items.map((row, index) => (
<DraggableRow
key={row.id}
id={row.id}
index={index}
moveRow={moveRow}
>
{columns.map((col) => (
<td key={col.key}>{row[col.dataIndex]}</td>
))}
</DraggableRow>
))}
</tbody>
</table>
</DndProvider>
);
};
使用react-beautiful-dnd替代方案
安装库:

npm install react-beautiful-dnd
实现代码:
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
const Table = ({ data, columns }) => {
const [items, setItems] = useState(data);
const onDragEnd = (result) => {
if (!result.destination) return;
const newItems = [...items];
const [reorderedItem] = newItems.splice(result.source.index, 1);
newItems.splice(result.destination.index, 0, reorderedItem);
setItems(newItems);
};
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="table">
{(provided) => (
<table {...provided.droppableProps} ref={provided.innerRef}>
<thead>
<tr>
{columns.map((col) => (
<th key={col.key}>{col.title}</th>
))}
</tr>
</thead>
<tbody>
{items.map((row, index) => (
<Draggable key={row.id} draggableId={row.id} index={index}>
{(provided) => (
<tr
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{columns.map((col) => (
<td key={col.key}>{row[col.dataIndex]}</td>
))}
</tr>
)}
</Draggable>
))}
{provided.placeholder}
</tbody>
</table>
)}
</Droppable>
</DragDropContext>
);
};
自定义实现方案
创建基础拖拽逻辑:

const useDragAndDrop = (initialData) => {
const [data, setData] = useState(initialData);
const [draggedItem, setDraggedItem] = useState(null);
const handleDragStart = (index) => {
setDraggedItem(data[index]);
};
const handleDragOver = (index) => {
if (draggedItem === null) return;
const draggedIndex = data.findIndex(item => item.id === draggedItem.id);
if (draggedIndex !== index) {
const newData = [...data];
newData.splice(draggedIndex, 1);
newData.splice(index, 0, draggedItem);
setData(newData);
}
};
return {
data,
handleDragStart,
handleDragOver,
};
};
在组件中使用:
const Table = ({ initialData, columns }) => {
const { data, handleDragStart, handleDragOver } = useDragAndDrop(initialData);
return (
<table>
<thead>
<tr>
{columns.map((col) => (
<th key={col.key}>{col.title}</th>
))}
</tr>
</thead>
<tbody>
{data.map((row, index) => (
<tr
key={row.id}
draggable
onDragStart={() => handleDragStart(index)}
onDragOver={(e) => {
e.preventDefault();
handleDragOver(index);
}}
>
{columns.map((col) => (
<td key={col.key}>{row[col.dataIndex]}</td>
))}
</tr>
))}
</tbody>
</table>
);
};
性能优化建议
对于大型表格,考虑使用虚拟滚动技术:
import { VariableSizeList as List } from 'react-window';
const Row = ({ index, style, data }) => {
const { moveRow, items, columns } = data;
return (
<DraggableRow
index={index}
id={items[index].id}
moveRow={moveRow}
style={style}
>
{columns.map((col) => (
<td key={col.key}>{items[index][col.dataIndex]}</td>
))}
</DraggableRow>
);
};
const VirtualizedTable = ({ data, columns }) => {
const [items, setItems] = useState(data);
const listRef = useRef();
const moveRow = (fromIndex, toIndex) => {
// ...相同的移动逻辑
listRef.current.resetAfterIndex(Math.min(fromIndex, toIndex));
};
return (
<DndProvider backend={HTML5Backend}>
<div style={{ height: '500px', overflow: 'auto' }}>
<List
ref={listRef}
height={500}
itemCount={items.length}
itemSize={() => 50} // 行高
width="100%"
itemData={{ moveRow, items, columns }}
>
{Row}
</List>
</div>
</DndProvider>
);
};






