You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Beautiful DND组件无法拖拽问题求助

React Beautiful DND 拖拽功能失效排查与修复

问题描述

使用React Beautiful DND实现看板拖拽功能,代码无报错但组件完全无法拖拽,需要排查原因并修复。

完整代码

导入部分

import React, { useState } from "react";
import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";

数据与函数定义

const tasks = [
  { id: "1", content: "First task" },
  { id: "2", content: "Second task" },
  { id: "3", content: "Third task" },
  { id: "4", content: "Fourth task" },
  { id: "5", content: "Fifth task" }
];

const taskStatus = {
  requested: {
    name: "Requested",
    items: tasks
  },
  toDo: {
    name: "To do",
    items: []
  },
  inProgress: {
    name: "In Progress",
    items: []
  },
  done: {
    name: "Done",
    items: []
  }
};

const onDragEnd = (result, columns, setColumns) => {
  if (!result.destination) return;
  const { source, destination } = result;

  if (source.droppableId !== destination.droppableId) {
    const sourceColumn = columns[source.droppableId];
    const destColumn = columns[destination.droppableId];
    const sourceItems = [...sourceColumn.items];
    const destItems = [...destColumn.items];
    const [removed] = sourceItems.splice(source.index, 1);
    destItems.splice(destination.index, 0, removed);
    setColumns({
      ...columns,
      [source.droppableId]: {
        ...sourceColumn,
        items: sourceItems
      },
      [destination.droppableId]: {
        ...destColumn,
        items: destItems
      }
    });
  } else {
    const column = columns[source.droppableId];
    const copiedItems = [...column.items];
    const [removed] = copiedItems.splice(source.index, 1);
    copiedItems.splice(destination.index, 0, removed);
    setColumns({
      ...columns,
      [source.droppableId]: {
        ...column,
        items: copiedItems
      }
    });
  }
};

组件渲染部分

function App() {
  const [columns, setColumns] = useState(taskStatus);
  return (
    <div>
      <h1 style={{ textAlign: "center" }}>Jira Board</h1>
      <div
        style={{ display: "flex", justifyContent: "center", height: "100%" }}
      >
        <DragDropContext
          onDragEnd={(result) => onDragEnd(result, columns, setColumns)}
        >
          {Object.entries(columns).map(([columnId, column], index) => {
            return (
              <div
                style={{
                  display: "flex",
                  flexDirection: "column",
                  alignItems: "center"
                }}
                key={columnId}
              >
                <h2>{column.name}</h2>
                <div style={{ margin: 8 }}>
                  <Droppable droppableId={columnId} key={columnId}>
                    {(provided, snapshot) => {
                      return (
                        <div
                          {...provided.droppableProps}
                          ref={provided.innerRef}
                          style={{
                            background: snapshot.isDraggingOver
                              ? "lightblue"
                              : "lightgrey",
                            padding: 4,
                            width: 250,
                            minHeight: 500
                          }}
                        >
                          {column.items.map((item, index) => {
                            return (
                              <Draggable
                                key={item.id}
                                draggableId={item.id}
                                index={index}
                              >
                                {(provided, snapshot) => {
                                  return (
                                    <div
                                      ref={provided.innerRef}
                                      {...provided.draggableProps}
                                      {...provided.dragHandleProps}
                                      style={{
                                        userSelect: "none",
                                        padding: 16,
                                        margin: "0 0 8px 0",
                                        minHeight: "50px",
                                        backgroundColor: snapshot.isDragging
                                          ? "#263B4A"
                                          : "#456C86",
                                        color: "white",
                                        ...provided.draggableProps.style
                                      }}
                                    >
                                      {item.content}
                                    </div>
                                  );
                                }}
                              </Draggable>
                            );
                          })}
                          {provided.placeholder}
                        </div>
                      );
                    }}
                  </Droppable>
                </div>
              </div>
            );
          })}
        </DragDropContext>
      </div>
    </div>
  );
}

export default App;

问题排查与修复方案

你的代码存在两个核心问题导致拖拽失效:

  1. 状态初始化的引用共享问题
    taskStatus.requested.items直接引用了tasks数组,useState(taskStatus)会保留这个原始引用,后续状态更新时可能因引用未变更导致组件不触发重新渲染。需要在初始化时创建数组副本:

    const [columns, setColumns] = useState(() => ({
      ...taskStatus,
      requested: {
        ...taskStatus.requested,
        items: [...tasks]
      }
    }));
    
  2. onDragEnd中的状态快照过时问题
    当前onDragEnd直接使用传入的columns参数,这是组件渲染时的旧状态快照,会导致状态更新逻辑基于过时数据执行。改用函数式更新可确保获取最新状态:

    const onDragEnd = (result) => {
      if (!result.destination) return;
      const { source, destination } = result;
    
      setColumns(prevColumns => {
        if (source.droppableId !== destination.droppableId) {
          const sourceColumn = prevColumns[source.droppableId];
          const destColumn = prevColumns[destination.droppableId];
          const sourceItems = [...sourceColumn.items];
          const destItems = [...destColumn.items];
          const [removed] = sourceItems.splice(source.index, 1);
          destItems.splice(destination.index, 0, removed);
          return {
            ...prevColumns,
            [source.droppableId]: {
              ...sourceColumn,
              items: sourceItems
            },
            [destination.droppableId]: {
              ...destColumn,
              items: destItems
            }
          };
        } else {
          const column = prevColumns[source.droppableId];
          const copiedItems = [...column.items];
          const [removed] = copiedItems.splice(source.index, 1);
          copiedItems.splice(destination.index, 0, removed);
          return {
            ...prevColumns,
            [source.droppableId]: {
              ...column,
              items: copiedItems
            }
          };
        }
      });
    };
    

    同时修改DragDropContext的onDragEnd绑定:

    <DragDropContext onDragEnd={onDragEnd}>
    

修改以上两处后,拖拽功能即可正常工作。


内容的提问来源于stack exchange,提问作者Vins98

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 14:17:14