4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

react dndでドラッグアンドドロップを作る!

Posted at

はじめに

現場でフォームのドラッグアンドドロップを検証する機会があり、Reactのdndライブラリを使ってみたので、共有します!

インストール

以下の公式ドキュメントに記載の通り、インストールを進めます。

npm install react-dnd react-dnd-html5-backend

実装コード全体

以下のコードで基本的にドラッグアンドドロップを実装することができます。

import React, { useState } from 'react';

interface Item {
  id: number;
  text: string;
}

const DragDropExample = () => {
  const [items, setItems] = useState<Item[]>([
    { id: 1, text: 'アイテム1' },
    { id: 2, text: 'アイテム2' },
    { id: 3, text: 'アイテム3' }
  ]);

  const handleDragStart = (e: React.DragEvent<HTMLDivElement>, id: number) => {
    e.dataTransfer.setData('text/plain', id.toString());
  };

  const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
    e.preventDefault();
  };

  const handleDrop = (e: React.DragEvent<HTMLDivElement>, targetId: number) => {
    e.preventDefault();
    const draggedId = parseInt(e.dataTransfer.getData('text/plain'));

    if (draggedId === targetId) return;

    const newItems = [...items];
    const draggedItem = newItems.find(item => item.id === draggedId);

    // draggedItemのnullチェックを追加
    if (!draggedItem) {
      console.error('ドラッグされたアイテムが見つかりません');
      return;
    }

    const targetIndex = newItems.findIndex(item => item.id === targetId);
    if (targetIndex === -1) {
      console.error('ターゲットアイテムが見つかりません');
      return;
    }

    // 元の位置から削除
    newItems.splice(newItems.findIndex(item => item.id === draggedId), 1);
    // 新しい位置に挿入
    newItems.splice(targetIndex, 0, draggedItem);

    setItems(newItems);
  };

  return (
    <div className="p-4">
      <h2 className="text-xl font-bold mb-4">ドラッグ&ドロップ例</h2>
      <div className="space-y-2">
        {items.map(item => (
          <div
            key={item.id}
            draggable
            onDragStart={(e) => handleDragStart(e, item.id)}
            onDragOver={handleDragOver}
            onDrop={(e) => handleDrop(e, item.id)}
            className="p-4 bg-white border border-gray-200 rounded shadow cursor-move hover:bg-gray-50"
          >
            {item.text}
          </div>
        ))}
      </div>
    </div>
  );
};

export default DragDropExample;


詳細説明

今回はTypescriptで書いているので、interfaceをセットします。
ドラッグアンドドロップではidが大切な要素になっており、このidを渡して要素の位置を変更していきます。

interface Item {
  id: number;    // 各アイテムを識別するためのID
  text: string;  // 表示するテキスト
}

初期値

useStateフックを使用して、基本的な要素を設定します。
今回は初期値として、以下の3アイテムをセットしました。

const [items, setItems] = useState<Item[]>([
  { id: 1, text: 'アイテム1' },
  { id: 2, text: 'アイテム2' },
  { id: 3, text: 'アイテム3' }
]);

ドラッグ開始の処理

ドラッグを開始したときに、そのアイテムのIDをdataTransferオブジェクトに保存します。

const handleDragStart = (e: React.DragEvent<HTMLDivElement>, id: number) => {
  e.dataTransfer.setData('text/plain', id.toString());
};

ドラッグオーバーの処理

デフォルトのドラッグオーバー動作を防ぐために必要で、これがないとドロップが機能しません。

const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
  e.preventDefault();
};

ドロップの処理

const handleDrop = (e: React.DragEvent<HTMLDivElement>, targetId: number) => {
  e.preventDefault();
  // ドラッグされたアイテムのIDを取得
  const draggedId = parseInt(e.dataTransfer.getData('text/plain'));
  
  // 同じアイテムへのドロップは無視
  if (draggedId === targetId) return;

  // 配列のコピーを作成
  const newItems = [...items];
  const draggedItem = newItems.find(item => item.id === draggedId);
  
  // エラーチェック
  if (!draggedItem) {
    console.error('ドラッグされたアイテムが見つかりません');
    return;
  }

  const targetIndex = newItems.findIndex(item => item.id === targetId);
  if (targetIndex === -1) {
    console.error('ターゲットアイテムが見つかりません');
    return;
  }
  
  // アイテムの位置を更新
  newItems.splice(newItems.findIndex(item => item.id === draggedId), 1);
  newItems.splice(targetIndex, 0, draggedItem);
  
  // 状態を更新
  setItems(newItems);
};

UI実装

最後に以下のUIを実装して完了です。

return (
  <div className="p-4">
    <h2 className="text-xl font-bold mb-4">ドラッグ&ドロップ例</h2>
    <div className="space-y-2">
      {items.map(item => (
        <div
          key={item.id}
          draggable           // ドラッグ可能にする
          onDragStart={(e) => handleDragStart(e, item.id)}
          onDragOver={handleDragOver}
          onDrop={(e) => handleDrop(e, item.id)}
          className="p-4 bg-white border border-gray-200 rounded shadow cursor-move hover:bg-gray-50"
        >
          {item.text}
        </div>
      ))}
    </div>
  </div>
);

おわりに

ドラッグアンドドロップはいくつか方法があるようなのですが、Reactを使用している場合はこのライブラリを使うことで比較的簡単に実装できることがわかりました。
参考になれば幸いです!

JISOUのメンバー募集中!

プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
実践的なカリキュラムで、あなたのエンジニアとしてのキャリアを最短で飛躍させましょう!
興味のある方は、ぜひホームページからお気軽にカウンセリングをお申し込みください!
▼▼▼

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?