当前位置:首页 > React

react实现table拖拽

2026-01-27 07:36:05React

实现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>
  );
};

性能优化建议

对于大型表格,考虑使用虚拟滚动技术:

react实现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>
  );
};

标签: 拖拽react
分享给朋友:

相关文章

vue实现拖拽改变尺寸

vue实现拖拽改变尺寸

Vue 实现拖拽改变尺寸的方法 使用原生 HTML5 拖拽 API 通过监听 mousedown、mousemove 和 mouseup 事件实现拖拽改变尺寸。创建一个可拖拽的边界元素,调整目标元素的…

react如何开发组件

react如何开发组件

React 组件开发基础 React 组件分为函数组件和类组件两种形式。函数组件是现代 React 开发的主流方式,结合 Hooks 可以实现完整功能。 函数组件示例: function Gree…

react如何配置

react如何配置

配置React项目的基本步骤 安装Node.js和npm 确保系统已安装Node.js(包含npm)。可通过命令行检查版本: node -v npm -v 创建React项目 使用官方工具Creat…

react如何引入echarts

react如何引入echarts

安装 ECharts 依赖 在 React 项目中安装 ECharts 核心库和 React 封装库: npm install echarts echarts-for-react 基础引入方式 创建…

react如何检测窗口

react如何检测窗口

监听窗口大小变化 使用useEffect钩子和resize事件监听窗口尺寸变化。在组件挂载时添加事件监听器,卸载时移除监听器以避免内存泄漏。 import { useState, useEffect…

react如何减少伤害

react如何减少伤害

减少 React 应用性能伤害的方法 使用 React.memo 或 PureComponent 对于函数组件,使用 React.memo 进行浅比较,避免不必要的重新渲染。类组件可以使用 PureC…