本記事では、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





コメント