Ext JS to React: Drag and Drop

   JavaScript
Ext JS to React: Drag and Drop

This is part of the Ext JS to React blog series. You can review the code from this article on the Ext JS to React Git repo.

Ext JS provided a set of drag and drop classes within the framework. Drag and drop within the more commonly used data-driven components was often handled through a plugin requiring no configuration on your part. Drag and drop implementations outside of grids and trees required you to handle the drag start and end operations and drops explicitly. In the React world you may run across view libraries that handle drag and drop interactions for you. However, if you are creating your own view components and want to offer drag and drop you’ll need to set up the logic for yourself. Since list / grid reordering likely represents one of the more common drag and drop use cases demonstrate how to can configure it yourself as a custom list component.

Note: While not a requirement for React, this article’s code examples assume you’re starting with a starter app generated by create-react-app.

React Simple List Example

For this article, we’re going to use the react-beautiful-dnd drag and drop library from Atlassian. To start, let’s create a simple list with three items that we can apply drag and drop to in order to demonstrate item reordering. Here is our simple React list:

import React, { Component } from 'react';
import './App.css';

const listCtStyle = {
  width: '200px',
  margin: '12px'
};

class App extends Component {
  render() {
    return (
      <div style={listCtStyle} className="list-ct">
        <div className="list-item">ITEM 1</div>
        <div className="list-item">ITEM 2</div>
        <div className="list-item">ITEM 3</div>
      </div>
    );
  }
}


export default App;

And here is the CSS we’ll use for our list:

.list-ct {
  background-color: #e2e2e2;
  display: flex;
  flex-direction: column;
  padding-bottom: 10px;
}
.list-item {
  padding: 6px 10px;
  margin: 10px 10px 0 10px;
  cursor: pointer;
  color: white;
  background: #e03d49;
}


Ext JS to React: Drag and Drop


React List Drag and Drop

With our simple list and its CSS rules created, we can modify the example to allow our list items to participate in drag and drop reordering. We’ll use react-beautiful-dnd’s runnable example for reference to add the necessary components and props to our list.

Install react-beautiful-dnd:

npm install --save npm i react-beautiful-dnd

src/App.js

import React, { Component } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import './App.css';

const listCtStyle = {
  width: '200px',
  margin: '12px'
};

const reorder =  (list, startIndex, endIndex) => {
  const result = Array.from(list);
  const [removed] = result.splice(startIndex, 1);
  result.splice(endIndex, 0, removed);

  return result;
};

class App extends Component {
  state = {
    items: [{
      id: 1
    }, {
      id: 2
    }, {
      id: 3
    }] 
  }

  handleDragEnd = (result) => {
    // dropped outside the list
    if(!result.destination) {
       return; 
    }
    
    const items = reorder(
      this.state.items, 
      result.source.index, 
      result.destination.index
    );
    
    this.setState({
      items
    });
  }

  render() {
    return (
      <DragDropContext onDragEnd={this.handleDragEnd}>
        <Droppable droppableId="droppable">
          {(provided, { isDraggingOver }) => (
            <div 
              ref={provided.innerRef} 
              style={{
                background: (isDraggingOver ? '#ffe1e3' : null),
                ...listCtStyle
              }}
              className="list-ct"
            >
              {this.state.items.map(({ id }) => (
                <Draggable key={id} draggableId={id}>
                  {(provided, { isDragging }) => (
                    <div>
                      <div
                        ref={provided.innerRef}
                        className="list-item"
                        {...provided.dragHandleProps}
                        style={{
                          background: (isDragging ? '#a7101b' : null),
                          ...provided.draggableProps.style
                        }}
                      >
                        ITEM {id}
                      </div>
                      {provided.placeholder}
                    </div>
                  )}
                </Draggable>
              ))}
            </div>
          )}
        </Droppable>
      </DragDropContext>
    );
  }
}

export default App;


Ext JS to React: Drag and Drop, List


Draggable List Explained

Let’s break down what we’re doing in this example:

  • After installing react-beautiful-dnd, we include the necessary components from its library
  • We’ll steal the reorder function from the react-beautiful-dnd working example to handle reordering the list items given the new order returned from the drag / drop operation (called reorder in the example above)
  • The list is wrapped with the DragDropContext component that handles the drop event and sets the reordered items on the list component state
  • Droppable follows and wraps our list container which we return from an arrow function
  • Our list container’s ref is set to provider.innerRef as required by react-beatiful-dnd
  • We append a background color to the container’s style object when the drag and drop handler internally registers that a drag operation is occurring over the list container (using the isDraggingOver param provided by the dnd library)
  • Next, we loop over the list component’s state items (an array of objects with a numeric id) adding a Draggable component for each item
  • Each Draggable is a div containing our item plus {provided.placeholder} which react-beautiful-dnd uses as a placeholder for our dragged item during the drag operation
  • Each item is given the requisite {provided.innerRef} and a few props via {...provided.dragHandleProps}
  • Finally, within the style prop we add a background color during the drag operation as well as include the specialized provided.draggableStyle style rules furnished by the dnd library

The end result is that you are now able to drag and drop items within the list and reorder them. In this example the new list order is recorded in the component’s state.items array, but if you wanted to persist the items order between page loads you could store the items order in LocalStorage or even ajax the order up to a server.

Alternative Drag and Drop Solutions

Our focus through this article was to demonstrate how to set up drag and drop for a list. However, if your requirement is not list or even list-to-list type drag and drop you may need to consider using a different drag and drop library or even writing your own solution using the HTML drag and drop events. The react-dnd library is a popular broad-purpose solution. One of the intended goals of react-dnd is to handle browser inconsistencies for you. Per React DnD:

HTML5 drag and drop has an awkward API full of pitfalls and browser inconsistencies. React DnD handles them internally for you, so you can focus on developing your application instead of working around the browser bugs.

Wrap Up

Setting up the logic to handle drag and drop can feel a bit onerous. Fortunately, there are a few drag and drop libraries in the React ecosystem that have made strides toward normalizing the experience between browsers and even between your various components. Have you discovered a drag and drop library we’ve not covered here? Have you set up a drag and drop solution you’re particularly proud of and would like to share? Please do so in the comments below!


Like What You See?

Got any questions?