如何使用状态变量在React/Kanban板中进行嵌套拖放,而不在React中工作



Context:我试图解决的主要问题是,停止onDragStart事件处理程序上的传播(通过e.stopPropagation(((会同时禁用拖放功能。然而,当我试图在状态中设置一个标志,以停止在父元素上触发的拖放事件;标志不起作用/状态没有及时设置或其他什么。

设置:一个看板样式的组件,具有包含可拖动卡片的可拖动列。

当您拖动一列并对其重新排序时,这些列能够正确呈现;onDragStart;事件,在状态中设置draggingIndex。然后,当另一列激发一个";onDragOver";事件中,列阵列被拼接,以将拖动的列从其原始位置移除,并将其插入到新的顺序中。这很好用。

当我试图拖动卡片而不是列时,问题就出现了。当";onDragStart;事件在一张牌上触发,我正在状态挂钩中设置一个标志。setDragCategory("卡片"(。列元素上的事件侦听器应该检查";dragCategory==="卡片";。如果是这样,他们应该退出函数,而不运行任何代码。

目标是,当您开始拖动列时,它的所有事件侦听器都会正常启动。但是,如果您开始在卡上拖动,列的事件侦听器基本上是通过在它们执行任何操作之前退出它们来停用的。

即使";onDragStart;卡上的处理程序首先运行(如果状态设置为dragCategory==="card",这并不能阻止列的事件处理程序运行。然后列的事件处理器设置dragCategory==="column"。因此,我试图拖动一张卡,但列正在重新排序。

我不明白为什么列的事件侦听器没有在发生这种情况之前退出它们的处理程序。

谢谢你的指点!

如果您将此代码直接粘贴到create-react-app项目的App.js文件中,则此代码应该可以工作。

App.js:

import React, { useState } from "react";
import { v4 as uuid } from "uuid";
import "./App.css";
const data = {};
data.columns = [
{ name: "zero", cards: [{ text: "card one" }, { text: "card two" }] },
{ name: "one", cards: [{ text: "card three" }, { text: "card four" }] },
{ name: "two", cards: [{ text: "card five" }, { text: "card six" }] },
{ name: "three", cards: [{ text: "card seven" }, { text: "card eight" }] },
{ name: "four", cards: [{ text: "card nine" }, { text: "card ten" }] },
{ name: "five", cards: [{ text: "card eleven" }, { text: "card twelve" }] },
];
function App() {
// when a card starts to drag, dragCategory is set to "card."  This is supposed to be a flag that will stop the columns' event listeners before they cause any changes in the state.  
let [dragCategory, setDragCategory] = useState(null);
// all of this is for reordering the columns. I have not gotten it to work well enough yet, to be able to write the state for reordering the cards:
let [columns, setColumns] = useState(data.columns);
let [draggingIndex, setDraggingIndex] = useState(null);
let [targetIndex, setTargetIndex] = useState(null);
return (
<div className="App">
<header>drag drop</header>
<div className="kanban">
{columns.map((column, i) => {
return (
<div
data-index={i}
onDragStart={(e) => {
console.log("column drag start");
// ERROR HERE: this function is supposed to exit here, if the drag event originated in a "card" component, but it is not exiting.
if (dragCategory === "card") {
e.preventDefault();
return null;
}
setDragCategory("column");
setDraggingIndex(i);
}}
// using onDragOver instead of onDragEnter because the onDragEnter handler causes the drop animation to return to the original place in the DOM instead of the current position that it should drop to.
onDragOver={(e) => {
if (dragCategory === "card") return null;
// allows the drop event
e.preventDefault();
setTargetIndex(i);
if (
dragCategory === "column" &&
targetIndex != null &&
targetIndex != draggingIndex
) {
let nextColumns = [...columns];
let currentItem = nextColumns[draggingIndex];
// remove current item
nextColumns.splice(draggingIndex, 1);
// insert item
nextColumns.splice(targetIndex, 0, currentItem);
setColumns(nextColumns);
setTargetIndex(i);
setDraggingIndex(i);
}
}}
onDragEnter={(e) => {}}
onDragEnd={(e) => {
setDragCategory(null);
}}
onDrop={(e) => {}}
className="column"
key={uuid()}
draggable={true}
>
{column.name}
{column.cards.map((card) => {
return (
<div
onDragStart={(e) => {

setDragCategory("card");
}}
key={uuid()}
className="card"
draggable={true}
>
{card.text}
</div>
);
})}
</div>
);
})}
</div>
</div>
);
}
export default App;

并将这个起始css粘贴到App.css文件中。

App.css

.kanban {
display: flex;
height: 90vh;
}
.column {
border: solid orange 0.2rem;
flex: 1;
}
.card {
height: 5rem;
width: 90%;
margin: auto;
margin-top: 2rem;
border: solid gray 0.2rem;
}

您面临这个问题是因为状态更新是异步的。

https://reactjs.org/docs/state-and-lifecycle.html#state-更新可能是异步

在onDragStart事件侦听器检查dragCategory === "card"时的列中,状态更改是否尚未发生。这就是为什么这个条件没有得到满足。

要解决您的问题,您需要在卡元素的onDragStart中添加event.stopPropagation((。这样,当你拖动一张牌时,你在DragStart上的列就不会触发。

像这样:

onDragStart={(e) => {
e.stopPropagation();
setDragCategory('card');
}}

此外,如果有多个相互依赖的状态,则减速器更合适。

https://reactjs.org/docs/hooks-reference.html#usereducer

我有一点时间,所以我使用reducer而不是state创建了一个代码沙盒来解决这个问题。

可以改进,但我没有更多的时间,我认为这可以让你走上正轨。

有时DragEnd不会为牌开火。我不明白为什么。

这就是为什么有时卡片会保持虚线拖动的风格。当这种情况发生时,它将停止正常工作:/

https://codesandbox.io/s/zealous-kate-ezmqj

编辑:以下是一个可能对您有所帮助的库:

https://github.com/atlassian/react-beautiful-dnd

以下是实现的库的示例:

https://react-beautiful-dnd.netlify.app/iframe.html?id=board--简单

最新更新