React Drag and Drop
So recently, I found myself working on a project that required me to implement a drag and drop feature. With no prior knowledge on how this works, first thing I did was to google things out, try to get context on how drag and drop even works.
I found out HTML5 already has this property, which made life easier but only if I was just building a webpage with no framework like React. I was able to walk my way through implementing it.
Moving forward, HTML5 has four properties to make drag and drop work; draggable, ondragstart, ondragover and ondrop.
Draggable, when added as an attribute to a HTML element, makes the target element to become draggable.
ondragstart, is called immediately the element dragged. Usually the content to be dragged about is set at this point.
ondragover, is added as an event listener to the element where the dragged content will be dropped. In other words, it is used to specify where the content is to be dropped.
ondrop, is called immediately the dragged content is released on the element that contains the ondragover event listener.
More details and explanation on these can be found here.
With all these known, implementing in react isn’t difficult as it might have sounded in the beginning right? 😉. So to implement this feature in react, we will be building a simple task manager like Trello 😎.
So for starters, we have our code block in the app.css and app.jsx file below
.App {
display: flex;
flex-direction: row;
}
.todos {
background: grey;
height: 100px;
width: 100px;
margin: 10px;
}
.done {
background: grey;
height: 100px;
width: 100px;
margin: 10px;
}
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: [
{
taskID: 1,
task: 'Walk the walk'
},
{
taskID: 2,
task: 'Talk the talk'
},
{
taskID: 3,
task: 'Jump the jump'
}
],
completedTasks: [],
draggedTask: {}
}
}
render() {
const { todos, completedTasks} = this.state;
return (
<div className="App">
<div className="todos">
{
todos.map(todo =>
<div
key={todo.taskID}
>
{todo.task}
</div>
)
}
</div>
<div
className="done"
>
{completedTasks.map((task, index) =>
<div
key={task.taskID}
>
{task.task}
</div>
)}
</div>
</div>
);
}
}
export default App;
So in the above code snippets, we have an array of todos, empty array of completedTasks in our state, and an empty object initialised in our state. Also, we have two main children divs (todos and done) contained in the parent div with className App. While the upper snippet contains the css.
The idea of this simple app is to move elements from the todos array to the completedTasks array and display the changes (which is what react does anyway 🤓).
First step is to make each child element of the todos div draggable by adding the draggable attribute onDrag event listener to the element like in the example below;
<div className="todos">
{
todos.map(todo =>
<div
key={todo.taskID}
draggable
onDrag={(event) => this.onDrag(event, todo)}
>
{todo.task}
</div>)
}
</div>
The draggable as said earlier enables the element which added to, to be dragged as needed while the onDrag as an event listener, calls the onDrag method when fired.
onDrag = (event, todo) => {
event.preventDefault();
this.setState({
draggedTask: todo
});
}
Above, the onDrag method takes in the event object and the dragged todo as parameters whenever it is called and stores the todo in the component’s state as draggedTask.
Next, we make the done div look like this;
<div
onDrop={event => this.onDrop(event)}
onDragOver={(event => this.onDragOver(event))}
className="done"
>
...
</div>
Following this, the following methods will be added to our component
onDragOver = (event) => {
event.preventDefault();
}
onDrop = (event ) => {
const { completedTasks, draggedTask, todos } = this.state;
this.setState({
completedTasks: [...completedTasks, draggedTask],
todos: todos.filter(task => task.taskID !== draggedTask.taskID),
draggedTask: {},
});
}
With this, when the item is released or dropped on the target done div, the onDrop method is fired courtesy of the event listener added earlier.
What this method simply does is get the previously stored task in the state, add it to the list of completedTasks, filters the todos list to remove this item as it has been moved and then assigns the initially set draggedTask to an empty object.
NB: All these are done in the called setState method.
Thank you for reading. Link to the full code can be found https://github.com/torkpe/dragNdrop/blob/master/src/App.js.