Create Kanban Board Using React

19-03-2019
source code

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