使用 React 进行拖放(无需库)修订版
简介
在本教程中,我将展示如何使用 React 从头开始创建无需第三方库的拖放组件。
我已经发表了一篇关于 React 中的拖放功能的文章,为什么还要再写一篇新的呢?
嗯,我们很多人学习新事物时,总以为自己已经理解了。但随着学习的深入,我们意识到还有很多东西需要学习。所以我用更好的方法制作了一个新的拖放功能。尽情享受吧!
重要信息
-
存在
e.dataTransfer
有助于拖放功能的事件处理程序,但由于我们使用 React,我发现使用状态更简单。 -
请务必查看代码沙盒。我可能会添加一些下面未反映的内容,但下面的代码是完整的。
-
你可能知道更好的方法!如果你觉得可以改进代码,请评论。
HTML5 中的拖放功能
我们将使用一些新元素,但我们不会使用所有的 HTML5 拖放元素。
draggable
使 div可拖动(而不是单击并拖动时突出显示)onDragStart
当你开始拖动时触发一次onDragEnter
当拖动的 div进入另一个 div 时触发一次。onDragOver
拖动到div上时持续触发onDrop
鼠标点击被释放时触发
我们将把其中的最后 4 个传递给 JavaScript,以赋予它 DND 逻辑。
入门。
让我们制作一些groups
可以拖动的元素和一些item
可以拖动的元素。
Dnd.js
import React, { useState } from "react";
import "./Dnd.scss";
export default function Dnd() {
// my groups to be dragged between
const groups = ["group1", "group2", "group3", "noDrop"];
// My items to be dragged around
const initialItems = [
{ id: 1, group: "group1", value: "drag 1" },
{ id: 2, group: "group1", value: "drag 2" },
{ id: 3, group: "group1", value: "drag 3" }
];
return (
<>
// Creating the group divs
<div className="groups">
{groups.map((group) => (
<div className="group">
<h1 className="title">{group}</h1>
<div>
// Creating our items to drag and drop
{items
.filter((item) => item.group === group)
.map((item) => (
<div
key={item.id}
id={item.id}
className="item"
// THIS MAKES THE ITEM DRAGGABLE!!!
draggable
>
// item title
{item.value}
</div>
))}
</div>
</div>
))}
</div>
</>
);
}
Dnd.scss
.groups {
display: flex;
margin: 5px;
padding: 5px;
flex-wrap: wrap;
.group {
margin: 2px;
padding: 20px;
min-height: 16rem;
background-color: green;
.title{
color: white;
padding: 0;
margin-top: 0;
}
}
}
.item {
background-color: yellow;
color: blue;
margin: 5px;
padding: 5px;
border: 2px green;
cursor: grab;
}
现在我们将添加事件和事件处理程序。请务必阅读代码中的注释,因为那里有详细的解释。我认为这比在代码之外描述所有内容更简单。
提示:在代码沙盒中注释更容易阅读。
Dnd.js
import React, { useState } from "react";
import "./Dnd.scss";
export default function Dnd() {
// Initial groups to drag between
const groups = ["group1", "group2", "group3", "noDrop"];
// Initial items to be dragged
const initialItems = [
{ id: 1, group: "group1", value: "drag 1" },
{ id: 2, group: "group1", value: "drag 2" },
{ id: 3, group: "group1", value: "drag 3" }
];
// Sets the state of the items. I may add an "add" function later
// Can be used to add items
const [items, setItems] = useState(initialItems);
// Data about a thing's id, origin, and destination
const [dragData, setDragData] = useState({});
// Are we hovering over the noDrop div?
const [noDrop, setNoDrop] = useState("");
// onDragStart we setDragData.
// useState instead of e.dataTransfer so we can transfer more data
const handleDragStart = (e, id, group) => {
setDragData({ id: id, initialGroup: group });
};
// If we enter the noDrop zone the state will be updated
// Used for styling.
const handleDragEnter = (e, group) => {
if (group === "noDrop") {
setNoDrop("noDrop");
}
};
// DND will not work without this.
const handleDragOver = (e) => {
e.preventDefault();
};
// setNoDrop to nothing to return styling to normal
const handleDragLeave = (e) => {
setNoDrop("");
};
// 1. makes copy of items (newItems)
// 2. changes category of the item to its new group
// 3. setItem to our NewItems
const changeCategory = (itemId, group) => {
const newItems = [...items];
newItems[itemId - 1].group = group;
setItems([...newItems]);
};
// 1. setNoDrop in case item was dropped in noDrop
// 2. gets the item id
// 3. doesn't allow drop in noDrop
// 4. changeCategory (see above)
const handleDrop = (e, group) => {
setNoDrop("");
const selected = dragData.id;
if (group === "noDrop") {
console.log("nuh uh");
} else {
changeCategory(selected, group);
}
};
return (
<>
<div className="groups">
{/* iterate over groups */}
{groups.map((group) => (
<div
// change styling if dragging into noDrop zone
className={`${
group === "noDrop" && noDrop === "noDrop" ? noDrop : "group"
}`}
// event handlers
onDragEnter={(e) => handleDragEnter(e, group)}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={(e) => handleDrop(e, group)}
key={group}
>
<h1 className="title">{group}</h1>
<div>
{/* iterate over items */}
{items
.filter((item) => item.group === group)
.map((item) => (
<div
key={item.id}
id={item.id}
// change style if dragged over noDrop
className={`${
group === "noDrop" && noDrop === "noDrop"
? "notAllowed"
: "item"
}`}
// MAKES THE ITEM DRAGGABLE!!!!
draggable
// event handler
onDragStart={(e) => handleDragStart(e, item.id, group)}
>
{/* The name of each item */}
{item.value}
</div>
))}
</div>
</div>
))}
</div>
</>
);
}
Dnd.scss
.groups {
display: flex;
margin: 5px;
padding: 5px;
flex-wrap: wrap;
.group {
margin: 2px;
padding: 20px;
min-height: 16rem;
background-color: green;
<span class="nc">.title</span><span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="no">white</span><span class="p">;</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">margin-top</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="p">}</span>
}
.noDrop {
margin: 2px;
padding: 20px;
min-height: 16rem;
background-color: red;
cursor: not-allowed !important;
<span class="nc">.title</span><span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="no">white</span><span class="p">;</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">margin-top</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="p">}</span>
}
}
.item {
background-color: yellow;
color: blue;
margin: 5px;
padding: 5px;
border: 2px green;
cursor: grab;
}
.notAllowed {
background-color: yellow;
color: blue;
margin: 5px;
padding: 5px;
border: 2px green;
cursor: not-allowed;
}
这就是它的样子
结论
这就是它的基本要点。如果你需要一些简单易用的东西,那就是它了,否则,你可以随意安装一个库。
观看实际操作!查看代码沙盒
文章来源:https://dev.to/wolfmath/drag-and-drop-with-react-519m