本記事では、react-beautiful-dnd
を使用して、ドラッグ&ドロップの並べ替え機能を実装する方法について解説する。react-beautiful-dnd
は、Reactで簡単にドラッグ&ドロップの機能を追加するためのもので、並べ替えやリストの管理に適している。react-beautiful-dnd
のインストールから、基本的な設定、並べ替え機能の実装、TypeScriptによる型定義までを詳しく説明する。
記事のゴール
以下のようなリストをドラッグ&ドロップで並べ替えれるようにする。
検証環境
今回の実装は以下の環境で検証を行っている。
react
: “^17.0.0 || ^18.0.0”next
: “14.2.12”typescript
: “^5”
インストール
まず、react-beautiful-dnd
をプロジェクトにインストールする。npm install
コマンドで、react-beautiful-dnd
と、TypeScript用の型定義を追加する。
$ npm install react-beautiful-dnd
$ npm install -D @types/react-beautiful-dnd
インストール後のpackage.jsonは以下のようになる。
"dependencies": {
"@types/react-beautiful-dnd": "^13.1.8",
"react-beautiful-dnd": "^13.1.1",
}
【結論】コードの全体像
index.tsx
以下が、ドラッグ&ドロップ並べ替え機能を実現するための全体コード。コピペで動作する。
import React, { useState } from "react";
import { DragDropContext, Droppable, Draggable, DropResult } from "react-beautiful-dnd";
const initialItems = [
{ id: "1", content: "Item 1" },
{ id: "2", content: "Item 2" },
{ id: "3", content: "Item 3" },
];
export default function Index() {
const [items, setItems] = useState(initialItems);
const onDragEnd = (
result: DropResult,
items: { id: string; content: string }[],
setItems: React.Dispatch<React.SetStateAction<{ id: string; content: string }[]>>
) => {
const { source, destination } = result;
// ドロップ先が存在しない場合、終了
if (!destination) return;
const reorderedItems = Array.from(items);
const [removed] = reorderedItems.splice(source.index, 1);
reorderedItems.splice(destination.index, 0, removed);
setItems(reorderedItems);
};
return (
<DragDropContext onDragEnd={(result) => onDragEnd(result, items, setItems)}>
<Droppable droppableId="droppable">
{(provided) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
style={{ padding: 20, width: 250 }}
>
{items.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={{
padding: 16,
marginBottom: 8,
backgroundColor: "#f2f2f2",
border: "1px solid #ddd",
borderRadius: 4,
...provided.draggableProps.style,
}}
>
{item.content}
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
}
コード解説
上記のコードは、react-beautiful-dnd
を用いてシンプルなドラッグ&ドロップの並べ替え機能を実装している。以下、コードの重要なポイントを詳しく解説する。
1. 必要なモジュールと型のインポート
import React, { useState } from "react";
import { DragDropContext, Droppable, Draggable, DropResult } from "react-beautiful-dnd";
DragDropContext
: ドラッグ&ドロップ機能のコンテキストを提供するコンポーネント。Droppable
: ドロップ可能な領域を定義するコンポーネント。Draggable
: ドラッグ可能な要素を定義するコンポーネント。DropResult
: ドラッグ&ドロップ操作の結果を表す型定義。
DragDropContext
DragDropContext
はドラッグ&ドロップ全体のコンテナであり、onDragEnd
プロパティでドラッグ完了時のイベントハンドラーを指定する。
これにより、ドラッグ完了後にどのようにリストを更新するかを制御できる。
<DragDropContext onDragEnd={(result) => onDragEnd(result, items, setItems)}>
...
</DragDropContext>
onDragEnd
:ドラッグ操作が終了したときに呼び出され、アイテムの並べ替え処理を行う関数。result
パラメータにはドラッグの詳細情報が含まれている。
Droppable
Droppable
コンポーネントは、アイテムをドロップできるエリアを定義する。
このコンポーネントの子要素には、リスト全体のレイアウトが含まれる。
<Droppable droppableId="droppable">
{(provided) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
style={{ padding: 20, width: 250 }}
>
...
{provided.placeholder}
</div>
)}
</Droppable>
droppableId
:Droppable
を識別するための一意なID。
今回の例では単一のリストのため「droppable」としている。provided.droppableProps
とprovided.innerRef
:react-beautiful-dnd
によって追加される属性で、Droppable
エリアの参照と必要な属性を設定する。provided.placeholder
:ドラッグ中にリストの見た目が崩れないように空白スペースを確保するための要素。
Draggable
Draggable
コンポーネントは、ドラッグ可能なアイテムを定義する。
各アイテムが一意に識別されるようにdraggableId
とindex
プロパティが必要。
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={{
padding: 16,
marginBottom: 8,
backgroundColor: "#f2f2f2",
border: "1px solid #ddd",
borderRadius: 4,
...provided.draggableProps.style,
}}
>
{item.content}
</div>
)}
</Draggable>
draggableId
:アイテムを一意に識別するためのID。provided.draggableProps
:アイテムがドラッグ可能であることを示すための属性。provided.dragHandleProps
:アイテムをドラッグするためのハンドル属性を提供する。これを使用することで、アイテム全体がドラッグ対象になる。
onDragEnd関数
onDragEnd
関数は、ドラッグ終了時に呼び出され、DropResult
オブジェクトを引数に受け取る。このオブジェクトには、source
(ドラッグ元の情報)とdestination
(ドロップ先の情報)が含まれており、これをもとにアイテムの並べ替えを行う。
const onDragEnd = (
result: DropResult,
items: { id: string; content: string }[],
setItems: React.Dispatch<React.SetStateAction<{ id: string; content: string }[]>>
) => {
const { source, destination } = result;
// ドロップ先が存在しない場合は処理を終了
if (!destination) return;
const reorderedItems = Array.from(items);
const [removed] = reorderedItems.splice(source.index, 1);
reorderedItems.splice(destination.index, 0, removed);
setItems(reorderedItems);
};
source
:ドラッグ元のインデックス位置を持つ情報。destination
:ドロップ先のインデックス位置を持つ情報。reorderedItems
:現在のリストを複製した配列。リストを直接変更するのではなく複製した配列を操作することで、不変性が保たれる。splice
メソッド:source.index
位置から1つのアイテムを削除し、destination.index
に挿入する。この操作によって、アイテムの並び順が変更される。
まとめ
この記事では、react-beautiful-dnd
を用いて、リストのドラッグ&ドロップによる並べ替え機能を実装した。DragDropContext
、Droppable
、Draggable
という3つの基本コンポーネントの役割を理解し、onDragEnd
関数を用いてアイテムの並べ替えを適切に制御できる。
react-beautiful-dnd
のGitHubリポジトリ: https://github.com/atlassian/react-beautiful-dnd
コメント