Drag and Drop Kanban board from scratch with React : Part 2 🚀
Published: 2023-12-29 09:42:26

In [part 1](https://dev.to/nasif2ahmed/drag-and-drop-kanban-board-from-scratch-with-react-1j9a) we discussed the basic idea of how to implement the drag and drop feature. In this part we will extend the idea and make the full Kanban board. Go read [part 1](https://dev.to/nasif2ahmed/drag-and-drop-kanban-board-from-scratch-with-react-1j9a) first if you haven't.

We are going to add two more columns for the kanban board. One for ongoing tasks and one for completed tasks

## Multiple States

Previously we only needed to maintain only one state `tasks` for todo list. But if we want to add two more columns or container for tasks to drop in then we need two more states. In total three separate states.

```tsx
const [todo, setTodo] = useState<string[]>();
const [ongoing, setOngoing] = useState<string[]>();
const [completed, setCompleted] = useState<string[]>();
```

So basically when we drop a task in Todo column it would get added to todo state and get deleted from previous state it was in and vice versa.

## Add two more columns

Lets now create ongoing and completed column alongside todo column. Which is very easy to do, we just have to copy paste the todo column's code and rename it. We should also replace old generic `handleOnDrop` function with their own functions.

```tsx
<div 
    className="..."
    onDragOver={handleOnDragOver}
    onDrop={handleOnDropOngoing}
>
    {ongoing &&
        ongoing.map((taskName) => {
            return (
                <div
                    className="..."
                    draggable
                    onDragStart={(e) => {
                        handleOnDrag(e, taskName);
                    }}
                >
                    {taskName}
                </div>
            );
        })}
</div>
```

and

```tsx
<div 
    className="..."
    onDragOver={handleOnDragOver}
    onDrop={handleOnDropCompleted}
>
    {completed &&
        completed.map((taskName) => {
            return (
                <div
                    className="..."
                    draggable
                    onDragStart={(e) => {
                        handleOnDrag(e, taskName);
                    }}
                >
                    {taskName}
                </div>
            );
        })}
</div>
```

You will notice we also changed the state name according to the states we created earlier and a new `onDrop` function for each column. So we also applied the changes to our old todo div

```tsx
<div 
    className="..."
    onDragOver={handleOnDrag}
    onDrop={handleOnDropTodo}
>
    {todo &&
        todo.map((taskName) => {
            return (
                <div
                    className="..."
                    draggable
                    onDragStart={(e) => {
                        handleOnDrag(e, taskName);
                    }}
                >
                    {taskName}
                </div>
            );
        })}
</div>
```
Now we have all the columns. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/buiseqtzv7w3jk42t0as.png)


## `onDrop` handlers

Removing the old `handleOnDrop` function, we now have three `onDrop` functions instead of one :  `handleOnDropTodo`, `handleOnDropOngoing` and `handleOnDropCompleted`

Now basically, there can be 3*2 = 6 scenarios. For example,

* Dropping in todo list from ongoing
* Dropping in ongoing list from todo
* Dropping in todo list from completed
* Dropping in completed list from todo
...... etc.

You get the idea.

Lets start with `handleOnDropTodo` function. Setting the task in the dropped column's state is same as before. New part is to delete the old instance from `ongoing` state if it came from ongoing or from `completed` state if it came from completed column.

The function would look like this :

```tsx
function handleOnDropTodo(e: React.DragEvent) {

    // Set the dropped task to todo state
    if (todo) {
        setTodo([
            ...todo.filter(
                (taskName) => taskName !== e.dataTransfer.getData("name")
            ),
            e.dataTransfer.getData("name"),
        ]);
    } else {
        setTodo([e.dataTransfer.getData("name")]);
    }

    // If dropping from ongoing --> todo
    // Delete from ongoing
    ongoing?.forEach((task) => {
        if (task === e.dataTransfer.getData("name")) {
            setOngoing([
                ...ongoing.filter(
                    (taskName) =>
                        taskName !== e.dataTransfer.getData("name")
                ),
            ]);
        }
    });

    // If dropping from completed --> todo
    // Delete from completed
    completed?.forEach((task) => {
        if (task === e.dataTransfer.getData("name")) {
            setCompleted([
                ...completed.filter(
                    (taskName) =>
                        taskName !== e.dataTransfer.getData("name")
                ),
            ]);
        }
    });
}

```

Now we have to write `handleOnDropOngoing` function. Which is pretty easy to do by copy pasting the code for `handleOnDropTodo` and changing the names.

```tsx
function handleOnDropOngoing(e: React.DragEvent) {
    // Set the dropped task to todo state
    if (ongoing) {
        setOngoing([
            ...ongoing.filter(
                (taskName) => taskName !== e.dataTransfer.getData("name")
            ),
            e.dataTransfer.getData("name"),
        ]);
    } else {
        setOngoing([e.dataTransfer.getData("name")]);
    }

    // If dropping from todo --> ongoing
    // Delete from todo
    todo?.forEach((task) => {
        if (task === e.dataTransfer.getData("name")) {
            setTodo([
                ...todo.filter(
                    (taskName) =>
                        taskName !== e.dataTransfer.getData("name")
                ),
            ]);
        }
    });

    // If dropping from completed --> ongoing
    // Delete from completed
    completed?.forEach((task) => {
        if (task === e.dataTransfer.getData("name")) {
            setCompleted([
                ...completed.filter(
                    (taskName) =>
                        taskName !== e.dataTransfer.getData("name")
                ),
            ]);
        }
    });
}
```

Lastly the `handleOnCompleted` function is easy to write following the last two, as we can see. So I am skipping it here.

We have all the states, column divs and `onDrop` handlers ready. Things should start working perfectly now.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5bmrlp8mn2bqqcpoo5jf.gif)



## Improvements

The code can be improved quite a lot. To keep things simple I wrote everything in one file and repeated codes.

* We can make a separate todo component with more info like deadline, priority, description etc.
* We can make hooks to handle the various dropping and deleting actions for states.
* Instead of already make task we can add a form to dynamically create tasks.
* Lastly, make all task data persistent by using localstorage or a database.

I already did all of these in a website I made using the same logic as this article called [Task Master](https://task-master-78bf8.web.app/) : 


![Task Master Website](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jnr4v30bsi7ydah4uybh.gif)

You can check the code for Task Master here : [Github Link](https://github.com/NasifAhmed/task-master-client/tree/master)


### Thank You 

**Link to the article's full code** : [Github Link](https://github.com/NasifAhmed/dnd-article)

I hope this was helpful to you 😊
Follow me on [Twitter](https://twitter.com/nasif2ahmed) and [LinkedIn](https://www.linkedin.com/in/nasif2ahmed/)
Thanks for reading 😇