Introduction
In this article I will be building a Kanban board using React. I will be utilising the built in drag and drop functionality in html5 to allow users to move tasks between columns
Application Structure
The app structure is as follows
public: contains app hosting
index.html
src
- Components: contains presentation components
- data: contains seed data for Kanban board
- services: contains columns-service which includes logic to move tasks between columns
Presentation Components
Column
The Column
component represent a kanban board column.
import React from 'react';
const Column = ({selectedColumn, dragOverHandler, dropHandler, children, highlight = false}) => {
const style = {
border: highlight ? '2px dashed red' : '2px solid #ccc'
};
return (
<div onDragOver={dragOverHandler}
onDrop={dropHandler}
className="column col-md-2 p-2 text-center"
style={style}>
<h4 className="title bg-info text-center text-white mb-3 py-1 text-uppercase">{selectedColumn.category}</h4>
{children}
</div>
);
};
export default Column;
This components receives the column object, handlers for drag over and drop as well as children. highlight
is a flag which is when true then a red dashed board will rendered around the column.
The {children}
is where the tasks will be rendered.
Task
The Task
component represents a single task. It receives task attributes like name and description. Also receives a dragStartHandler
.
import React from "react";
const Task = ({name, description, color, dragStartHandler}) => {
const taskStyle = {width: '18rem', backgroundColor: color};
return (
<div onDragStart={dragStartHandler}
draggable
style={taskStyle} className="w-100 py-1 mb-2">
<div className="font-weight-bold border-bottom">
{name}
</div>
<div>
{description}
</div>
</div>
);
};
export default Task;
Header
This is a presentation component which holds h1
tag and receives title as an attribute.
import React from 'react';
const Header = ({title}) => {
return (
<h1 className='bg-dark text-center text-white mb-5 py-3'>
{title}
</h1>
);
};
export default Header;
Smart Component
The application contains one smart component which is App
. This component is responsible for managing the state of the application as well as bootstrapping other presentation components.
Drag and Drop events
onDragStart = (ev, taskId, colId) => {
this.setState({draggedItem: {taskId, colId}});
ev.dataTransfer.setDragImage(ev.target, 20, 20);
};
onDragOver = (ev, colId) => {
ev.preventDefault();
if (this.state.draggedItem.targetColId === colId) {
return;
}
this.setState(() => {
return {
draggedItem: {...this.state.draggedItem, targetColId: colId}}
});
};
onDrop = (ev, targetColId) => {
ev.preventDefault();
if (this.state.draggedItem.colId === targetColId) {
this.setState({
draggedItem: {}
});
return;
}
moveTask(this.state.draggedItem.colId, this.state.draggedItem.taskId, targetColId);
this.setState({
columns: getColumns(),
draggedItem: {}
});
};
onDragStart
I am storing information about the current task being dragged.
In onDragOver
I am first checking to see if the task is being dragged on its own column, if this is the case then do nothing otherwise update state to add a new property called targetColId
, This property will be used later to highlight the column with a dashed border.
In onDrop
I am checking if the task is dropped on the same column, if this is the case then I clear the state and do nothing. If it is a new column then I call moveTask
then update states accordingly by getting the new columns using getColumns()
column.
render
The render
method will be called by react after each state update. I am utilising the shouldComponentUpdate
method to reduce the time render
method is being called. In shouldComponentUpdate
I check if the targetColumnId
was changed in order to call render otherwise there is no need to update. What this means is when the user drag a task over a new column then render
will be called but until the task is moved over a new column then there is no need to call render
as there is nothing to update.
The body of render is bootstrapping the view using the other presentation components. I am building a list of columns and for each column I am calling getTasks
passing the column id so I can get the tasks for this column.
Conclusion
In this article I went though the steps I took to build a kanban board using React with the help of the built in drag and drop feature in html5