React DnDで学ぶドラッグ&ドロップの実装手順
はじめに
Webアプリケーションを扱ううえで、ドラッグ&ドロップ機能はユーザーにとってわかりやすい操作感を実現するうえで欠かせないかもしれません。 ファイルのアップロードやタスクの並び替えなどで見かけることが多いですよね。 Reactにはさまざまなドラッグ&ドロップのライブラリがありますが、React DnDは拡張性が高く、かなり柔軟にカスタマイズできる点が特徴です。
とはいえ、初心者の方からすると「ライブラリをどうやって使いこなすのだろう?」と不安になることもあるでしょう。 ここでは、React DnDを使ったドラッグ&ドロップ実装の基本から応用までを順を追って解説します。 具体的なサンプルコードや、実務への応用のしかたも取り上げますので、これからドラッグ&ドロップを導入したいと考える方の参考になればうれしいです。
React DnDとは何か
React DnDは、React向けに設計されたドラッグ&ドロップ専用のライブラリです。 ドラッグ対象とドロップ対象を明確に分けて管理できるため、複雑な要件にも対応できます。
他にも有名なライブラリとしてReact Beautiful DnDがありますが、そちらは見た目のアニメーションが標準実装される一方で、設定の自由度が限られる場面もあるかもしれません。 React DnDはアニメーションの提供こそ少ないですが、その分カスタマイズ性が高いという特徴があります。 そのため、固有のデザイン要件や独自のデータ構造を扱うときに選ばれることが多いです。
仕組みと特徴
React DnDでは、ドラッグを可能にする要素をDragSource、ドロップを受け取る要素をDropTargetと呼ぶことが多かった時期がありました。 現在はフックAPIのuseDragやuseDropにより、より直感的な書き方ができるようになっています。 これらのフックは、ドラッグ&ドロップの状態管理を簡単に行うための機能を提供します。
React DnDを使う場合は、アプリケーション全体にDndProviderというコンポーネントを配置し、必要に応じてHTML5のバックエンドやタッチデバイス用のバックエンドを指定します。 これによって、マウス操作やタッチ操作といった異なる操作体系にも対応できます。 フロントエンドでよくある複数デバイス対応のケースにも活用しやすいというのはありがたいですよね。
なぜReactでDnDを実装するのか
皆さんは、テーブルやリストを並び替える機能を作るとき、どのようなアプローチを考えるでしょうか。 純粋なJavaScriptだけでも作れますが、ドラッグ時の要素の見た目変更やステート管理、ドロップ後のデータ更新などを考えると、かなりの手間になるかもしれません。
しかし、React DnDを使えば、Drag and Dropに関する共通処理をライブラリ側がサポートしてくれます。 Reactのコンポーネント構造に沿う形で実装できるので、コードが自然と整理されやすいです。 UIの更新もReactの再レンダリングに任せればよいので、複雑なDOM操作を手動で行う必要もありません。
React DnDのメリット
コンポーネントごとに責務が明確
ドラッグ要素側とドロップ要素側のコードが分割されるので見通しが良いです。
フックAPIによる柔軟な実装
useDragやuseDropで、各コンポーネントがどのように振る舞うかを細かく制御できます。
マルチバックエンド対応
HTML5だけでなくタッチ操作用のバックエンドにも切り替え可能です。
ほかのライブラリとの比較
React Beautiful DnDのように、デフォルトの見た目やアニメーションが整っているものもあります。 ただし、それゆえに配置場所やスタイルが制限される場合もあるので、複雑なUIにはReact DnDが向いているかもしれません。 一方で、シンプルにリストを並び替えるだけならReact Beautiful DnDが手軽でしょう。 こうした特徴の違いを理解して、自分のプロジェクトに合ったライブラリを選ぶことが大切です。
React DnDの基本的な使い方
ここでは、React DnDを導入する際の流れを簡単に紹介します。 細かい設定項目は多いですが、まずは全体像を掴んでしまうと理解しやすくなります。
インストール手順
React DnDを利用するには、ライブラリ本体とバックエンドのインストールが必要です。
最新版では、HTML5のドラッグ&ドロップを使用する場合にreact-dnd-html5-backend
を導入します。
以下のようにコマンドを実行してください。
npm install react-dnd react-dnd-html5-backend
場合によってはyarnやpnpmを使っている方もいるでしょうが、コマンド自体は同様の形で導入できます。
DndProviderの設定
React DnDを使うには、アプリケーション全体をDndProviderでラップする必要があります。 DndProviderにはバックエンドを指定し、ドラッグ操作のハンドリングを行えるようにします。 例として、HTML5バックエンドを使う場合の設定方法は下記の通りです。
import React from "react"; import ReactDOM from "react-dom/client"; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; import App from "./App"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <DndProvider backend={HTML5Backend}> <App /> </DndProvider> );
ここで、backend={HTML5Backend}
を指定することで、ブラウザのHTML5 APIを利用したドラッグ&ドロップの処理が可能になります。
こうすることでReactコンポーネントからドラッグとドロップのイベントをフックできるようになるのです。
useDragフックとuseDropフック
React DnDの要は、useDragとuseDropというフックです。 これらのフックを使うことで、コンポーネントをドラッグ可能にしたり、他のコンポーネントへドロップさせたりできます。
useDrag
ドラッグ対象の要素を作るためのフックです。 引数にオブジェクトを渡して、typeやitemなどのプロパティを設定することで、ドラッグ情報をカスタマイズできます。
useDrop
ドロップ先を作るためのフックです。
ドロップ時にどのような処理を行うかをdrop
プロパティなどで指定できます。
それぞれのフックはドラッグ状態やドロップの結果を管理するため、Reactのステート管理とスムーズに連動できる点が魅力でしょう。
サンプル実装
それでは簡単なサンプルを見てみましょう。 一つのアイテムをドラッグして、ドロップエリアに移動するようなケースです。 まずはドラッグ可能なコンポーネントを用意します。
import React from "react"; import { useDrag } from "react-dnd"; function DraggableItem({ text }) { const [{ isDragging }, dragRef] = useDrag({ type: "ITEM", item: { text }, collect: (monitor) => ({ isDragging: monitor.isDragging(), }), }); return ( <div ref={dragRef} style={{ opacity: isDragging ? 0.5 : 1, border: "1px solid #333", padding: "8px", marginBottom: "4px", cursor: "move", }} > {text} </div> ); } export default DraggableItem;
ここでは、type: "ITEM"
でアイテムの種類を指定し、item: { text }
でドラッグ時に渡したいデータを定義しています。
isDragging
でドラッグ中のスタイルを変化させ、視覚的にわかりやすくしています。
次に、ドロップ領域を受け持つコンポーネントを作成します。
import React from "react"; import { useDrop } from "react-dnd"; function DropArea({ onDropItem }) { const [{ canDrop, isOver }, dropRef] = useDrop({ accept: "ITEM", drop: (item) => { onDropItem(item.text); }, collect: (monitor) => ({ canDrop: monitor.canDrop(), isOver: monitor.isOver(), }), }); return ( <div ref={dropRef} style={{ border: "2px dashed #999", height: "100px", display: "flex", alignItems: "center", justifyContent: "center", backgroundColor: isOver ? "#fafafa" : "#fff", }} > {canDrop ? "ここにドロップ" : "ドロップエリア"} </div> ); } export default DropArea;
accept: "ITEM"
で、どのタイプのアイテムを受け取れるかを指定しています。
drop
コールバックでは、ドロップされたときの処理を記述し、そこに渡ってきたitem
の情報を使うイメージです。
canDrop
やisOver
などを使うと、ドロップエリアの見た目を変えることも簡単です。
最後に、上記の2つをまとめてAppコンポーネントで読み込み、動作を確認できます。
アイテムをドラッグしてドロップエリアに落とすと、onDropItem
が呼び出され、実際の処理を行えるわけです。
実務での活用シーン
React DnDは、タスク管理アプリやEコマースサイトの商品並び替えなどでよく活用されています。 リストの要素をドラッグ&ドロップで並び替えたいときや、カード形式のUIを動的に移動させたいときなどにとても便利です。 また、複数のドロップ先を用意してアイテムを仕分ける、という複雑な操作もわりと簡単に実装できます。
一方で、チーム開発の現場では、独自のUIルールやデザインガイドラインに合わせる必要が出てくるでしょう。 React DnDは見た目の強制力があまりないので、カスタムデザインがやりやすいです。 そのため、スタイルフレームワークやCSS Modules、Tailwindなどを組み合わせて使うケースが多いです。
React DnDは、レイアウトの自由度を求めるプロジェクトで活用しやすいです。
よくあるトラブルシューティング
ドラッグ&ドロップ実装で起こりがちな問題としては、ドラッグ要素が予期せずテキスト選択状態になってしまうケースが挙げられます。
これはCSSでuser-select: none;
を適切に設定すると回避できる場合があります。
ドラッグ中に画像やテキストが選択されると、ユーザーが違和感を覚えることもあるため、必要に応じてスタイルを調整すると良いでしょう。
また、モバイル端末ではPC向けのHTML5 Backendが動作しないことがあるため、別のバックエンドを用意する必要があります。
例えば、react-dnd-touch-backend
を導入して、DndProviderで切り替えることでタッチ操作にも対応できます。
モバイル端末向けにドラッグ&ドロップを実装する場合は、HTML5以外のバックエンドの検討が必要です。
実装のポイントとベストプラクティス
React DnDを使うときは、まずデータ構造を明確にすることが重要だと考える人も多いでしょう。 ドラッグ&ドロップで移動させたいアイテムの情報がどこに格納されるのか、ドロップ先でどんな状態更新を行うのか、あらかじめ整理しておくとスムーズです。
さらに、複数のドロップ先を用意するケースでは、type
を細かく分けることで混乱を避けやすいです。
たとえば、タスクリストとアーカイブリストで異なるtypeを指定するなど、どのリストにドロップしたかを正しく判定できるようにしておくといいでしょう。
パフォーマンスへの配慮
複数のドラッグ要素やドロップ領域を用意すると、ステートやコンテキストの更新が増えます。
高頻度で再レンダリングされるケースも考えられるため、メモ化を使って不要な再レンダリングを減らす工夫が必要です。
Reactのmemo
やuseCallback
をうまく活用すると、アプリが大きくなってもドラッグ操作が重くなりにくいです。
まとめ
React DnDは、シンプルなドラッグ&ドロップから複数要素を扱う複雑なケースまで対応しやすいライブラリです。 Reactのコンポーネントやフックと相性が良いため、ドラッグ&ドロップの処理を必要に応じて細かく制御できます。 特にUIやレイアウトを独自にカスタマイズする必要がある場面では、有力な選択肢となるでしょう。
実務ではタスク管理やカード配置、Eコマースなど、多彩なシーンでDrag and Dropの操作が求められます。 React DnDを活用すれば、見た目の自由度を保ちながら、直感的な操作性を提供しやすいです。 最初はセットアップやフックの使い方に戸惑うかもしれませんが、基本を押さえてしまえば複雑な要件にも対応しやすいと感じるはずです。
ぜひReact DnDを活用して、ドラッグ&ドロップのあるReactアプリケーションを作ってみてはいかがでしょうか。 ライブラリの仕組みをよく理解し、UI/UXを向上させる第一歩にしてもらえればと思います。