您是正在寻找 react-beautiful-dnd 的替代方案的开发人员吗?不要再看了!DND 工具包在这里,我们将在本演练指南中讨论如何使用它。
D试剂盒作为React-Beautiful-DND的替代品
用于设计 Web 界面的最流行的 UI 模式之一是拖放模式。它是一种易于使用且直观的模式,可以包含在项目中,最常用于上传文件以及重新排序或移动项目等过程。
有几个软件包使实现拖放功能变得简单,React 开发人员中流行的选择是 react-beautiful-dnd。
不幸的是,它不再维护,未来没有任何开发计划。这让像我这样的开发人员寻找一个可靠的替代方案,也给了我另一个不喜欢 Atlassian 的理由(第一个是 Jira!
输入 DND 套件。
什么是 dnd 套件?
DND Kit是块上的新“套件”(这个笑话归咎于创作者),对于那些寻求React-beautiful-DND替代品的人来说,它看起来很有希望。
在本教程中,Windows 11电脑声卡驱动怎么安装?教你五种方法轻松搞定我们将创建一个基本的看板,同时了解 dnd 套件及其功能。
以下是我们完成后董事会的外观图像:
开始使用 dnd 套件
我们将首先使用 Create React App 设置一windows 11怎么看电脑显卡?个 React 项目。为了简单起见,我们将使用 Chakra UI 组件库,因为它易于设置和使用。
要创建和设置 React 项目,请运行以下命令:
npx create-react-app react-kanban-app --template @chakra-ui # create project cd react-kanban-app # move into the project directory # If you prefer TS over JS and yarn over npm npx create-react-app react-kanban-app --template @chakra-ui/typescript yarn create react-app react-kanban-app --template @chakra-ui yarn create react-app react-kanban-app --template @chakra-ui/typescript
这将创建一个名为 的文件夹。使用命令行 cd 到项目目录并运行:react-kanban-app
npm run start
这将在端口 3000 上启动应用。在chrome浏览器占用太多内存怎么办?解决Chrome吃内存硬伤浏览器中打开,您将看到以下屏幕:localhost:3000
安装dnd-kit/core
现在我们已经设置了一个基础项目,让我们从安装 dnd kit 开始;我们今天将使用的轻量级、高性能和可扩展的 React 拖放工具包。
要安装软件包,请运行:
npm install @dnd-kit/core # If you run into peer dependencies issues (esp. npm 7+) npm install --legacy-peer-deps @dnd-kit/core
此命令将从 dnd-kit 安装软件包。core
该软件包附带了在 React 应用程序中创建WhatsApp电脑版二维码不显示怎么办?9种快速解决教程拖放功能所需的构建块。该软件包附带 、 和核心组件。coreDndContext
Draggable``Droppable
除此之外,它还附带一个组件,以更流畅的外观改善用户体验——我们将在本文后面更详细地介绍核心组件。DragOverlay
对于更高级的用例,我们还可以安装 dnd kit 提供的其他软件包。
修饰 符
该软件包附带了可用于更改核心组件行为的有用修饰符。
以下是修饰符的一些主要功能:
-
将运动限制为单个轴(水平或垂直)
-
将运动限制为窗口或可拖动项目的父元素
-
将可拖动项目对齐到网格
-
创建自定义修饰符
预设(可排序预设)
dnd-kit 工具包附带一个可排序的预设。此安卓手机通话记录不显示怎么办?修复它的10种方法预设可用于在 React 中构建可排序的拖放界面。
在本教程中,我们将坚持使用核心包,因此,在我们动手之前,让我们仔细看看它。
dnd 套件的构建块
DndContext
这是我们拖放功能的根组件,所有其他块都嵌套在里面。
该组件接受大约十几个 prop,这些 prop 有助于在特定事件发生时修改行为或运行代码。
出于本教程的目的,我们将使用 和 props。collisionDetection``onDragEnd
注意,了解更多关于其他道具的信息。
可拖动
核心包导出钩子,可以在我们的 React 组件中使用它来使其成为可拖动的组件(稍后会详细介绍)。useDraggable
//exampleDraggable.jsx import {useDraggable} from "@dnd-kit/core" import {CSS} from "@dnd-kit/utilities" export const MyDraggableComponent = () => { const {attributes, listeners, setNodeRef, transfrom} = useDraggable({ id: 'draggable-1', data: { .... parent: 'ToDo', title: 'Complete blogpost.' .... } return <div {...attributes} {...listeners} ref={setNodeRef} styles={ {transfrom: CSS.Translate.toString(transform) }}>Drag Me!</div> })
可滴落
就像钩子一样,我们可以使用钩子使我们的 React 组件成为可丢弃的目标。useDraggable``useDroppable
//exampleDroppable.jsx import {useDroppable} from "@dnd-kit/core" export const MyDroppableComponent = () => { const {setNodeRef} = useDroppable({ id: 'droppable-1' }) return <div ref={setNodeRef}> Drop on me! </div> }
传感器
传感器是不同的输入方趣知笔记法,可用于启动可拖动项的拖动。
我们可以使用一些内置传感器:
-
指针
-
键盘
-
触摸
-
鼠
默认值为 和传感器。如果要使用其他传感器,可以通过初始化传感器然后将其传递给 来完成此操作。DndContextPointer
Keyboard``DndContext
//sensors.jsx import {MouseSensor, TouchSensor, useSensor} from '@dnd-kit/core'; export const DragDropContainer = () => { const mouseSensor = useSensor(MouseSensor); // Initialize mouse sensor const touchSensor = useSensor(TouchSensor); // Initialize touch sensor const sensors = useSensors(mouseSensor, touchSensor) return (<DndContext sensors={sensors}>.....</DndContext>) // Pass the 2 sensors }
现在我们已经介绍了这些基础,现在我们可以开始使用 dnd kit 构建看板了,所以让我们直接进入它。
上面显示的是我们将要构建的看板的组件分解。
我们今天将探讨三个主要组成部分:
-
KanbanCard:可拖放到可放置区域中的可拖动看板Item
-
KanbanLane:可放置的可放置区域KanbanCard
-
KanbanBoard:将所有内容固定在一起的组件
KanbanCard元件
让我们从组件开始:KanbanCard
// KanbanCard.tsx import { Flex, Text } from "@chakra-ui/react"; import { useDraggable } from "@dnd-kit/core"; import { CSS } from "@dnd-kit/utilities"; const KanbanCard = ({ title, index, parent, }: { title: string; index: number; parent: string; }) => { const { attributes, listeners, setNodeRef, transform } = useDraggable({ id: title, data: { title, index, parent, }, }); const style = { transform: CSS.Translate.toString(transform), }; return ( <Flex padding="3" backgroundColor="white" margin="2" borderRadius="8" border="2px solid gray.500" boxShadow="0px 0px 5px 2px #2121213b" transform={style.transform} {...listeners} {...attributes} ref={setNodeRef} > <Text>{title}</Text> </Flex> ); };
这里有几点需要注意,我将在下面重点介绍。
该组件需要三个道具:
-
title:卡片标题
-
index:当前通道中卡的索引
-
parent:卡当前所在的车道名称
要使组件可拖动,我们必须使用钩子。在趣知笔记网站地图上面的例子中,我们需要传递一些东西作为参数:useDraggable
-
id:用于标识DndContext
-
data:可在事件处理程序中使用的数据
钩子还返回了许多我们必须考虑的事情:
-
attributes:需要添加到可拖动 DOM 节点的辅助功能属性
-
listeners:拖动工作需要许多事件处理程序
-
setNodeRef:dnd-kit 用来跟踪 DOM 节点的函数
-
transform:保存位置并缩放可拖动元素值的对象
最后,为了直观地更新组件,我们必须更新卡片的 CSS 属性。为了获取卡片位置,我们需要将钩子返回的转换值传递给 dnd-kit 提供的辅助函数。transform``useDraggable
KanbanLane元件
现在,让我们看一下组件:KanbanLane
// KanbanLane.tsx import { Flex, Text } from "@chakra-ui/react"; import { useDroppable } from "@dnd-kit/core"; interface KanbanLaneProps { title: string; items: Cards[]; } export default function KanbanLane({ title, items }: KanbanLaneProps) { const { setNodeRef } = useDroppable({ id: title, }); return ( <Flex flex="3" padding="5" flexDirection="column" minH="10rem"> <Text fontWeight="bold">{title}</Text> <Flex ref={setNodeRef} backgroundColor="gray.200" borderRadius="8" flex="1" padding="2" flexDirection="column" > {items.map(({ title: cardTitle }, key) => ( <KanbanCard title={cardTitle} key={key} index={key} parent={title} /> ))} </Flex> </Flex> ); }
这是一个非常精简的组件,因为它所做的只是渲染多个组件。需要注意的一件事是它使用钩子,这使它成为一个可放置的区域。KanbanCard``useDroppable
我们需要传入 a 中唯一的 .id``DndContext
KanbanBoard元件
最后,让我们仔细看看将它们联系在一起的组件:KanbanBoard
// KanbanBoard.tsx import { DndContext, rectIntersection } from "@dnd-kit/core"; import KanbanLane from "./KanbanLane"; import AddCard from "./AddCard"; import { Flex } from "@chakra-ui/react"; import { useState } from "react"; import { Cards } from "./types"; export default function KanbanBoard() { const [todoItems, setTodoItems] = useState<Array<Cards>>([]); const [doneItems, setDoneItems] = useState<Array<Cards>>([]); const [inProgressItems, setInProgressItems] = useState<Array<Cards>>([]); const [uItems, setuItems] = useState<Array<Cards>>([]); const addNewCard = (title: string) => { setuItems([...uItems, { title }]); }; return ( <DndContext collisionDetection={rectIntersection} onDragEnd={(e) => { const container = e.over?.id; const title = e.active.data.current?.title ?? ""; const index = e.active.data.current?.index ?? 0; const parent = e.active.data.current?.parent ?? "ToDo"; if (container === "ToDo") { setTodoItems([...todoItems, { title }]); } else if (container === "Done") { setDoneItems([...doneItems, { title }]); } else if (container === "Unassigned") { setuItems([...uItems, { title }]); } else { setInProgressItems([...inProgressItems, { title }]); } if (parent === "ToDo") { setTodoItems([ ...todoItems.slice(0, index), ...todoItems.slice(index + 1), ]); } else if (parent === "Done") { setDoneItems([ ...doneItems.slice(0, index), ...doneItems.slice(index + 1), ]); } else if (parent === "Unassigned") { setuItems([...uItems.slice(0, index), ...uItems.slice(index + 1)]); } else { setInProgressItems([ ...inProgressItems.slice(0, index), ...inProgressItems.slice(index + 1), ]); } }} > <Flex flexDirection="column"> <AddCard addCard={addNewCard} /> <Flex flex="3"> <KanbanLane title="ToDo" items={todoItems} /> <KanbanLane title="In Progress" items={inProgressItems} /> <KanbanLane title="Done" items={doneItems} /> <KanbanLane title="Unassigned" items={uItems} /> </Flex> </Flex> </DndContext> );
这里发生了很多事情;让我们一一回顾一下。
首先,该板有四个通道:、、 和 。ToDoIn Progress
Done``Unassigned
其次,组件只是一个带有按钮的文本框。单击该按钮时,将创建具有文本框中指定标题的卡片并将其添加到通道中。<AddCard/>``Unassigned
DndContext位于组件的根目录下,我们需要向其传递两个重要的道具:
-
collisionDetection:这决定了使用哪种方法来检测可拖动组件和可放置组件之间的冲突
-
onDragEnd
:这是一个事件处理程序,每次我们停止拖动可拖动组件时都会运行。在事件处理程序中:
-
我们识别卡所在的车道并将其删除
-
我们确定卡片掉落在哪个车道上,并将其添加到该车道
-
最后,这就是它的样子
结论
虽然react-beautiful-dnd没有得到积极的维护,但它更成熟,周围有一个庞大的社区。正因为如此,说服人们跳槽肯定是具有挑战性的。它还具有一些高级功能,例如支持多个拖动、虚拟列表支持和 dnd 套件不提供开箱即用的 SSR。
但是,我觉得dnd kit有可能达到与react-beautiful-dnd相同的功能,甚至超越这一点。有兴趣的人可以通过解决问题和打开 PR 来帮助实现这一目标!