Re-ordering the List

Before we get started, lets install react-beautiful-dnd package as a dependency.

$ yarn add react-beautiful-dnd

The react-beautiful-dnd package consists three components.

DragDropContext — This component enables the both drag and drop function in the app.

— This component enables the both drag and drop function in the app. Droppable — This component will create a region where we can drop things.

— This component will create a region where we can drop things. Draggable — This component will turn things in our app draggable.

Start by importing the DragDropContext into the App/index.js file.

import {DragDropContext} from 'react-beautiful-dnd';

We will then use this component to wrap the code that renders the columns in our app.

<DragDropContext>

{this.state.columnsort.map(columnId => {

const column = this.state.columns[columnId];

const heroes = column.heroId.map(heroId => this.state.heroes[heroId]);

return <Column key={Column.id} column={column} heroes={heroes} />;

})}

</DragDropContext>

Let’s leave this as it is for now and go update the Column component. Start by importing the Droppable component from react-beautiful-dnd package and use to wrap the div with classname of Hero-List . The Droppable component also has one prop named droppableId . The Droppable component also expects its child to be a function that returns a React component. A great advantage here is that React does not need to create any new DOM nodes.

The function here will take an argument called provided . This argument is an object that serves a number of purposes:

droppableProps are props that need to be applied to any component that you want to deem as draggable .

are that need to be applied to any component that you want to deem as . innerRef is a function that is used to supply the DOM node of the component to the react-beautiful-dnd package.

So update the Droppable component with the following code:

<Droppable droppableId={this.props.column.id}>

{(provided) => (

<div

className="Hero-List"

innerRef={provided.innerRef}

{...provided.droppableProps}

>

{this.props.heroes.map((hero, index) => (

<Hero key={hero.id} hero={hero} index={index} />

))}

{provided.placeholder}

</div>

)}

</Droppable>

The placeholder is React element that is used to increase the available space in a droppable component while a drag is going on.

Next, we will go to the Hero component and make it draggable. Start by importing the Draggable component into the file.

import {Draggable} from 'react-beautiful-dnd';

Wrap the div with className of Container with this Draggable component. The Draggable component also has two props : The draggableId and index .

<Draggable

draggableId={this.props.hero.id}

index={this.props.index}

>

<div className="Container">

{this.props.hero.name}

</div>

</Draggable>

Similar to the Droppable component, the Draggable also expects its child to be a component. So let’s take care of it by rewriting the Draggable component as follows:

<Draggable

draggableId={this.props.hero.id}

index={this.props.index}

>

{provided => (

<div className="Container"

{...provided.draggableProps}

{...provided.dragHandleProps}

innerRef={provided.innerRef}

>

{this.props.hero.name}

</div>

)}

</Draggable>

You will now see that we can drag things around in our column like this:

But Ooops! We now see that the list doesn’t set to the new order of objects and reverts back to how it originally was.

The DragDropContext has three callbacks:

onDragStart — What to do when drag starts.

— What to do when drag starts. onDragUpdate — What to do when something changes during a drag.

— What to do when something changes during a drag. onDragEnd — What to do when drag ends.

I only need the onDragEnd callback. Let’s add it to the DragDropContext component inside the src/App :

<DragDropContext onDragEnd={this.onDragEnd}>

Then create the onDragEnd helper function inside the same class as shown below:

onDragEnd = result => {

const {destination, source, draggableId} = result;

if (!destination) {

return;

}

if (

destination.droppableId === source.droppableId &&

destination.index === source.index

) {

return;

}

const column = this.state.columns[source.droppableId];

const newHeroIds = Array.from(column.heroIds);

newHeroIds.splice(source.index, 1);

newHeroIds.splice(destination.index, 0, draggableId);

const newColumn = {

...column,

heroIds: newHeroIds,

};

const newState = {

...this.state,

columns: {

...this.state.columns,

[newColumn.id]: newColumn,

},

};

this.setState(newState);

};

Here, the source and destination objects contain the info about where our Draggable started and finished.

The first conditional statement is for the case where there is no destination . In such cases, we don’t have to do anything, so we simply return nothing.

Next, we need to check it the location of the draggable has changed. To do that we check whether the droppableId and index of the destination and source are the same. If they are, then that mean the location has not changed, and we don’t need to do anything again.

Then we are re-ordering the heroIds array for that column . First we extract the column from the state, then I will create a new array and store it with the same data that we just extracted from the state.

After that, I am moving the heroId from its old index to the new index using splice method. I will then use splice to insert the draggableId , which is actually the heroId .