Drag and Drop (DnD) is a user interface concept using which one can select an object/element in the viewport and can move it (ie drag) to a desired location on the view port (ie drop).

Drag and Drop (DnD) makes it easy to copy, move, reorder and delete items with the help of mouse clicks. It is much more intuitive from the point of user experience than other user actions which may require clicking, typing etc. Often, the UI gets simpler and cleaner on account of using drag & drop.

Some of the different use-cases include uploading files, rearranging data in a table/section like in a Trello board, matching values in one section to an other value in another section.

React DnD

HTML5’s Drag and Drop API supports drag and drop operations in a HTML application. DnD operations involve moving around objects in the screen. They are inherently complex and there are few libraries which help us make our work much simpler. React DnD is a popular library with MIT license and around 18.1k stars in GitHub at the time of writing this article.

The Quest for a better UX using DnD

At Sixt R&D, we had business requirements which demanded a lot of complex operations. They could be made much simpler using DnD operations which are more intuitive to the users.

Required Drag and Drop Actions

Our UX design demanded the below drag and drop operations between different sections like Dragging a row from a table into the body of an accordion in a section which has a list of accordions. Rows to be draggable based on validation with some existing data. Dragging a marker located on top of Google Map into the body of an accordion in a section which has a list of accordions. Markers to be draggable based on validation with some existing data. This was coupled with validations on the draggable objects as well as the droppable zones.

We solved these requirements elegantly using React DnD.

Working with React DnD

React DnD is a simple to use library which can be installed as below.

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

React-dnd requires a ‘backend’ to work. We will go with HTML5Backend, the most popular one. There are other third party backends like touch backend which are also available. Here, we will stick to HTML5Backend.

Setting up the Context for Drag and Drop

In order to use the Drag and Drop functionalities, we need to enclose the components with the DndProvider component. It is usually done near the top of the application tree. This will inject the DndProvider component with the property ‘backend’ having value as HTML5Backend.

import { HTML5Backend } from 'react-dnd-html5-backend'
import { DndProvider } from 'react-dnd'

export default class MyApplication{
  render() {
    return (
      <DndProvider backend={HTML5Backend}>
        /* Our Drag-and-Drop Application */
      </DndProvider>
    )
  }
}

useDrag Hook

In order to make a component draggable, you can use the useDrag hook provided by the React Dnd library. It can be used as below :

const [, drag] = useDrag(() => ({
    type: type,
    item: {},
    canDrag: true,
  }
), [dependency]);

useDrag returns a drag object which needs to set to the object that is to be dragged like below.

<div ref={drag}>
  ....
</div>

useDrag hook accepts a range of properties. Some of the important ones are listed below :-

  • type 

    Type of the dragged item. This is helpful to distinguish between draggable elements, when there are different types of draggable objects that behave differently when dragged.

  • item

    When dropped, this object will appear as input to the callback function drop supplied to the useDrop hook.

  • canDrag

    When set to true, the object is draggable. When false, it is not draggable. It can be used when we need to control draggability of an object.

When the dependency value changes, the values are recomputed within the useDrag hook. It is useful in scenarios when the draggability of an object changes. Failure to update this, will lead to canDrag and item values not getting updated when required.

useDrop Hook

In order to make an element a container into which dragged objects can be dropped, we can use the useDrop hook.

const [, drop] = useDrop(
    () => ({
      accept: ['type 1','type 2',],
      canDrop: () => true,
      drop: (item) => {
          ......
      }
    }
),[dependency]);

// here item is the value passed to drag object property 'item'

useDrop returns a drop object which needs to be set to the DOM object that will behave as the container into which objects will be dragged like below.

<div ref={drop}>
  ....
</div>

useDrop hook accepts a range of properties. Some of the important ones are listed below :-

  • accept

    It accepts an array of item types that can be dragged into the drop element. 

  • drop 

    It accepts a callback function that gets triggered when a drop action occurs. It takes an input object, which will be populated with the item object passed as a property to the dragged object.

  • canDrop

    When set to true, dragged object can be dropped into the DOM element. It can be used when we need to control if a dragged object can be dropped into a particular drop container.

When the dependency value changes, the values are recomputed within the useDrop hook. Failure to update this, will lead to canDrop and other properties inside useDrop hook not getting updated when required.

React Dnd supports more such features which make it a truly versatile library that can used to create complex apps that require a lot of conditional drag and drop options. Do give it a try if you need to implement such functionalities. 

Happy coding !