How to Use Beautiful DND Drag and Drop with React?

Tags: react, tutorial, programming, reactnative
Published: 2021-10-22 10:36:27

It’s for sure as an expert React Developer you will be familiar with react libraries. They make the development process very easy and contribute to saving time.  One such library is the beautiful DND that stands for Drag and Drop by Atlassian. It could be considered a higher level abstraction built for horizontal and vertical lists.

Often people think Drag and Drop is no longer a big deal. However, it is still a part of importance. It enables you to transfer the items between drag tables, list, animations etc. No doubt it sounds simple but it allows you to take an object to the desired location. Thus it helps in saving time.  

Here we will take the kids game as a react drag and drop example.
Main Concepts of React DND:

<b>There are three main concepts of DND library which are as follows</b>.

<b>DragDropContext</b>: It is defined as a place where drag and drop action occurs. When the draggable item is dropped then this component calls onDragEnd. When an item is dragged, professionals could even define onDragStart and onDragUpdate to be executed.

<b>Droppable</b>: This is the component where we take the elements to drag and where we drop them.There is a requirement of many properties here.

<b>Draggable</b>: This is defined as the item to be moved. It also requires many properties.

<b>Children</b>: The content of the dataItem

We now let’s define the initial data to create the game.

const initialData = {
  column: {
	id: 'column-1',
      numberIds: ['five', 'one', 'four', 'two', 'three'],
	},
    numbers: {
      'four': { id: 'four', content: '4' },
      'fife': { id: 'fife', content: '5' },
      'three': { id: 'one', content: '3' },
      'one': { id: 'three', content: '1' },
      'two': { id: 'two', content: '2' },
	}
};
export default initialData;
We are now starting to make our first component. This will contain a render method.
class OurNumbersGame extends React.Component<any, IOurNumbersGameState>{
  public constructor(props: any) {
    super(props);
    this.onDragEnd = this.onDragEnd.bind(this);
    this.state = {...initialData};
  }
  public onDragEnd(result: any) {
	// the item was dropped!
  }
  public render() {
  const numbers = this.state.column.numberIds.map((numberId: string) => this.state.numbers[numberId]);
    return (
      <OurNumbersGameContext onDragEnd={this.onDragEnd}>
        <VerticalColumn column={this.state.column} items={numbers} />
      </OurNumbersGameContext>
	)
  }
}
OurNumbersGameContext is just a wrapper of DragDropContext. The column that contains the draggable elements is known as VerticalColumn.
export default (props: IVerticalColumnProps) =>
  <DroppableWrapper droppableId={props.column.id} className="source">
    <DraggableListItems items={props.items} />
  </DroppableWrapper>
DroppableWrapper is the component that abstracts the idea of a droppable container. It’s all because of a particular property.
export default (props: any) =>
  <Droppable droppableId={props.droppableId}>
    {(provided: any) => (
      <div className={props.className}
            ref={provided.innerRef}
            {...provided.droppableProps}
            {...provided.droppablePlaceholder}>
              {props.children}
      </div>
	)}
  </Droppable>

There is a need for a droppableId for Droppable and it must be unique inside the DragDropContext.

Now, provided is the first argument of this function. DroppableProps are also included in this argument, which are essential properties that are needed to designate the component as a droppable. The innerRef is the property defined as a function used to supply your DOM element to the library.
We will also insert the placeholder which is defined as a React Element. This hikes the space availability when needed. This needed to be added as a component child.
Now we will code our DraggableListItems and this will create the NumberBox.

export default (props: IDraggableListItems) =>
  <div> {props.items.map(toNumberBox)} </div>
function toNumberBox(item: INumberItemProps, position: number) {
  return <NumberBox key={item.id}
                	className="box"
                	itemPosition={position}
                	value={item.id}
                	content={item.content} />
}
NumberBox will define the wrapper to Draggable.
export default (props: IDraggableItem) => {
const className = `dnd-number size-${props.value}`; 
  return (
    <DraggableItemWrapper draggableId={props.value}
                          index={props.itemPosition}
                      	className={className}>
      <div>{props.content}</div>
    </DraggableItemWrapper>
  )
}

We have now created a DraggableItemWrapper and this will abstract the idea of Draggable.
export default (props: any) =>
  <Draggable draggableId={props.draggableId} index={props.index}>
    {(provided: any) => (
      <div className={props.className}
           ref={provided.innerRef}
           {...provided.draggableProps}
           {...provided.dragHandleProps}>
        {props.children}
      </div>
	)}
  </Draggable>

The props pattern will be done now. In this case, there will be a need for draggableID and index in the Draggable. This draggableID should be unique inside the DragDropContext.
So we have defined the main abstractions of the main concepts of the library as follows.
OurNumbersGameContext: This is an abstraction of DragDropContext
DraggableItemWrapper: This is an abstraction of the Draggable
DroppableWrapper: This is an abstraction of Droppable
First we will have to analyze the argument like this
result: {
   destination: {
     droppableId: "column-1"
     index: 2
   }
   draggableId: "four"
   reason: "DROP"
   source: {
     droppableId: "column-1"
     index: 0
   }
   type: "DEFAULT"
 }

Now it will contain the information about where you are dragging on and where you are dropping on.

With all this data we can do the following.

First, save destination, draggableId and source in the variable.
const { destination, source, draggableId  }  = result;
Now creating a new list of sorted numbers:
const column = this.state.column;
const numberIds = Array.from(column.numberIds);
numberIds.splice(source.index, 1);
numberIds.splice(destination.index, 0, draggableId);
const numbers = numberIds.map((numberId: string) =>
  parseInt(this.state.numbers[numberId].content, 10));
Now update the state:
const newColumn = {
  ...column,
  numberIds
};
this.setState({
  ...this.state,
  column: newColumn
});
When the user will drag a number or win then we can play an already downloaded sound.
public playSound(numbers: number[]) {
  const sound = isSortedAsc(numbers) ? ClapsSound : MoveSound;
  new Audio(sound).play();
}
The final method will look like this.
Public onDragEnd(result: any) {
  const { destination, source, draggableId } = result;
  if (!destination) { return }
 
  const column = this.state.column;
  const numberIds = Array.from(column.numberIds);
  numberIds.splice(source.index, 1);
  numberIds.splice(destination.index, 0, draggableId);
  const numbers = numberIds.map((numberId: string) =>
    parseInt(this.state.numbers[numberId].content, 15));
 
  this.playSound(numbers);
  this.updateState(column, numberIds);
}


<b>Wrapping Up</b>:

So here we have explained DND React library for your knowledge. However, it not ends here only because there is a wide scope of this library. We urge developers to use this library in their projects, practice the code with different experiments. It saves time to the great extent and thus also contributes to productivity. Thanks for reading and happy coding. Meanwhile, if you are looking to <a href="https://latitudetechnolabs.com/hire-dedicated-resources/">hire react developer</a> then we are here to assist you.