react中使用dnd-kit實現拖拽排序

厚礼蝎發表於2024-07-31

dnd-kit

官網

入口: https://ant-design.antgroup.com/docs/react/recommendation-cn
倉庫: https://github.com/clauderic/dnd-kit
官網: https://dndkit.com/

介紹

DnD-Kit(Drag and Drop Kit)是一個用於構建可拖放使用者介面的React庫。
它被設計為輕量級、模組化、高效能,並且易於整合到現有的React應用程式中。
DnD-Kit的主要目標是提供一個簡單而強大的API來處理拖放操作,同時保持高度的可定製性和可訪問性。

主要特點

  • 輕量級:DnD-Kit僅包含實現拖放功能所必需的核心程式碼,這使得它的體積小且載入速度快。
  • 模組化:庫被分解成多個獨立的模組,可以根據需要選擇性地匯入所需的元件或功能。
  • 高效能:透過最佳化演算法和利用React的特性,DnD-Kit能夠高效地處理複雜的拖放場景。
  • 可訪問性:DnD-Kit遵循WCAG指南並支援ARIA屬性,確保拖放操作對於所有使用者都是可訪問的。
  • 可擴充套件性:開發者可以透過自定義感測器、識別器等來擴充套件DnD-Kit的功能,以適應特定的應用需求。

安裝

npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/modifiers @dnd-kit/utilities --save
  • @dnd-kit/core:核心庫,提供基本的拖拽功能。
  • @dnd-kit/sortable:擴充套件庫,提供排序功能和工具。
  • @dnd-kit/modifiers:修飾庫,提供拖拽行為的限制和修飾功能。
  • @dnd-kit/utilities:工具庫,提供 CSS 和實用工具函式。

使用

最基礎的上下文

需要先使用 <DndContext /> 元件包裹需要實現拖拽效果的元件,相當於可拖拽的範圍容器

import React from 'react';
import {DndContext} from '@dnd-kit/core';

// 這些是實現的各種元件
import {Draggable} from './Draggable';
import {Droppable} from './Droppable';

function App() {
  return (
    <DndContext>
       // 元件包裹在內
      <Draggable />
      <Droppable />
    </DndContext>
  )
}

示例

匯入必要包

import type { DragEndEvent } from "@dnd-kit/core";
import { DndContext } from "@dnd-kit/core";
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
import {
    arrayMove,
    SortableContext,
    useSortable,
    /*
    垂直列表使用verticalListSortingStrategy,
	橫向列表使用horizontalListSortingStrategy
	*/
    verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Card } from "antd";
import * as React from "react";
import { useState } from "react";

準備資料

interface ItemType {
    id: number;
    name: string;
}

const App = () => {
    const data: ItemType[] = [
        { id: 1, name: "列表項1" },
        { id: 2, name: "列表項2" },
        { id: 3, name: "列表項3" },
        { id: 4, name: "列表項4" },
    ];
    const [items, setItems] = useState(data);
    return (
        <>
            // 元件部分
            // ......
        </>
    );
};

export default App;

實現一個可拖拽元件

interface ItemType {
    id: number;
    name: string;
}

const DraggableListNode = (props: ItemType) => {
    const { attributes, listeners, setNodeRef, transform, transition } = useSortable({
        id: props.id,
    });

    const style = {
        transform: CSS.Transform.toString(transform),
        transition,
        marginTop: 8,
    };

    return (
        <Card bordered style={style} ref={setNodeRef} {...attributes} {...listeners}>
            // 這裡簡單展示了name
            {props.name}
        </Card>
    );
};

在元件中應用拖拽元件

const App= () => {
    // 這是拖拽結束的響應方法
    const handleDragEnd = ({ active, over }: DragEndEvent) => {
        console.log(active);
        console.log(over);
        // 當拖拽前後的id不一致時,說明完成了有效的拖拽行為
        if (active.id !== over?.id) {
            // 對資料進行修改
            const activeIndex = items.findIndex((i) => i.id === active.id);
            const overIndex = items.findIndex((i) => i.id === over?.id);
            const newlist = arrayMove(items, activeIndex, overIndex);
            setItems(newlist);
        }
    };
    return (
        <>
            <DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={handleDragEnd}>
                <SortableContext
                    items={items.map((i: ItemType) => i.id)}
                    // 設定豎向拖拽
                    strategy={verticalListSortingStrategy}
                >
                    {items.map((item: ItemType) => (
                        <DraggableListNode key={item.id} {...item} />
                    ))}
                </SortableContext>
            </DndContext>
        </>
    );
};

export default App;

完整程式碼

import type { DragEndEvent } from "@dnd-kit/core";
import { DndContext } from "@dnd-kit/core";
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
import {
    arrayMove,
    SortableContext,
    useSortable,
    /*
    垂直列表使用verticalListSortingStrategy,
	橫向列表使用horizontalListSortingStrategy
	*/
    verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Card } from "antd";
import * as React from "react";
import { useState } from "react";

interface ItemType {
    id: number;
    name: string;
}

const DraggableListNode = (props: ItemType) => {
    const { attributes, listeners, setNodeRef, transform, transition } = useSortable({
        id: props.id,
    });

    const style = {
        transform: CSS.Transform.toString(transform),
        transition,
        marginTop: 8,
    };

    return (
        <Card bordered style={style} ref={setNodeRef} {...attributes} {...listeners}>
            {props.name}
        </Card>
    );
};

const App= () => {
    const data: ItemType[] = [
        { id: 1, name: "列表項1" },
        { id: 2, name: "列表項2" },
        { id: 3, name: "列表項3" },
        { id: 4, name: "列表項4" },
    ];
    const [items, setItems] = useState(data);
    const handleDragEnd = ({ active, over }: DragEndEvent) => {
        console.log(active);
        console.log(over);
        if (active.id !== over?.id) {
            console.log("active", active.id);
            const activeIndex = items.findIndex((i) => i.id === active.id);
            const overIndex = items.findIndex((i) => i.id === over?.id);
            const newlist = arrayMove(items, activeIndex, overIndex);
            setItems(newlist);
        }
    };
    return (
        <>
            <DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={handleDragEnd}>
                <SortableContext
                    items={items.map((i: ItemType) => i.id)}
                    strategy={verticalListSortingStrategy}
                >
                    {items.map((item: ItemType) => (
                        <DraggableListNode key={item.id} {...item} />
                    ))}
                </SortableContext>
            </DndContext>
        </>
    );
};

export default App;

相關文章