使用 dnd 套件和 React 构建看板

您是正在寻找 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种快速解决教程拖放功能所需的构建块。该软件包附带 、 和核心组件。coreDndContextDraggable``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>
}

传感器

传感器是不同的输入方趣知笔记法,可用于启动可拖动项的拖动。

我们可以使用一些内置传感器:

  • 指针

  • 键盘

  • 触摸

默认值为 和传感器。如果要使用其他传感器,可以通过初始化传感器然后将其传递给 来完成此操作。DndContextPointerKeyboard``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 ProgressDone``Unassigned

其次,组件只是一个带有按钮的文本框。单击该按钮时,将创建具有文本框中指定标题的卡片并将其添加到通道中。<AddCard/>``Unassigned

DndContext位于组件的根目录下,我们需要向其传递两个重要的道具:

  • collisionDetection:这决定了使用哪种方法来检测可拖动组件和可放置组件之间的冲突

  • onDragEnd

    :这是一个事件处理程序,每次我们停止拖动可拖动组件时都会运行。在事件处理程序中:

    • 我们识别卡所在的车道并将其删除

    • 我们确定卡片掉落在哪个车道上,并将其添加到该车道

最后,这就是它的样子

结论

虽然react-beautiful-dnd没有得到积极的维护,但它更成熟,周围有一个庞大的社区。正因为如此,说服人们跳槽肯定是具有挑战性的。它还具有一些高级功能,例如支持多个拖动、虚拟列表支持和 dnd 套件不提供开箱即用的 SSR。

但是,我觉得dnd kit有可能达到与react-beautiful-dnd相同的功能,甚至超越这一点。有兴趣的人可以通过解决问题和打开 PR 来帮助实现这一目标!

猜你喜欢

转载自blog.csdn.net/weixin_47967031/article/details/132706373