Reactでドラッグ&ドロップでListの並び替えをする

# はじめに

こんにちは、エンジニアのkeitaMaxです。

https://dndkit.com/

こちらのライブラリを使用して、ドラッグ&ドロップで並び替えできるリストを作成しようと思います。

Next.jsとTailwindCSSを使用しています。

# インストール

以下をインストールします。

```
npm install @dnd-kit/core @dnd-kit/sortable
```

# 実装

ファイル構成は以下のようにしました。

```
/
└─ src/views
      └─ DragDropView
         ├─ index.ts
         ├─ index.stories.tsx
         └─ components/SortableItem
            └─ index.ts
```

```tsx:DragDropView/index.tsx

import { DndContext, useSensor, useSensors } from '@dnd-kit/core'
import React, { useState } from 'react'
import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy } from '@dnd-kit/sortable'
import {
  closestCenter,
  KeyboardSensor,
  PointerSensor,
} from "@dnd-kit/core"
import { SortableItem } from './components/SortableItem'

export const DragDropView = React.memo(function DragDropView() {
  const [items, setItems] = useState(["Item 1", "Item 2", "Item 3", "Item 4"])

  const sensors = useSensors(
    useSensor(PointerSensor),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    })
  )

  const handleDragEnd = (event: any) => {
    const { active, over } = event

    if (active.id !== over.id) {
      setItems((prevItems) => {
        const oldIndex = prevItems.indexOf(active.id)
        const newIndex = prevItems.indexOf(over.id)

        return arrayMove(prevItems, oldIndex, newIndex)
      })
    }
  }

  return (
    <DndContext
      sensors={sensors}
      collisionDetection={closestCenter}
      onDragEnd={handleDragEnd}
    >
      <SortableContext items={items} strategy={verticalListSortingStrategy}>
        <ul className="mx-auto max-w-md rounded bg-gray-100 p-4 shadow">
          {items.map((item) => (
            <SortableItem key={item} id={item} />
          ))}
        </ul>
      </SortableContext>
    </DndContext>
  )
})
export default DragDropView

```

```tsx:DragDropView/index.stories.tsx
import { Meta, StoryObj } from '@storybook/react'
import { DragDropView } from '.'

const meta: Meta<typeof DragDropView> = {
  component: DragDropView,
  tags: ['autodocs'],
}
export default meta

type Story = StoryObj<typeof meta>

export const Default: Story = {}
```

```SortableItem/index.tsx
import React from "react"
import { useSortable } from "@dnd-kit/sortable"
import { CSS } from "@dnd-kit/utilities"

interface SortableItemProps {
  id: string;
}

export const SortableItem: React.FC<SortableItemProps> = ({ id }) => {
  const { attributes, listeners, setNodeRef, transform, transition } =
    useSortable({ id })

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

  return (
    <li
      ref={setNodeRef}
      style={style}
      {...attributes}
      {...listeners}
      className="mb-2 cursor-pointer rounded bg-white p-4 shadow hover:bg-gray-50"
    >
      {id}
    </li>
  )
}

```

これをnpm run storybookをして確認します。

# 挙動

![draggablegif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715123/cec83ee3-fdfc-8de5-43a3-f7024aca6464.gif)

こんな感じでドラッグ&ドロップで動かせるようなリストが完成しました！


# おわりに

この記事での質問や、間違っている、もっといい方法があるといったご意見などありましたらご指摘していただけると幸いです。

最後まで読んでいただきありがとうございました！

# 参考

https://dndkit.com/