Published 2 months ago

Build a Sortable React Kanban Board with Optimistic Updates

Software Development
Build a Sortable React Kanban Board with Optimistic Updates

Build a Sleek & Sortable Drag-and-Drop Kanban in React – No Hassle!

Building a Kanban board with drag-and-drop functionality in React can be surprisingly complex, especially when integrating with a backend system. One major challenge is handling optimistic updates, where UI changes are reflected immediately, pending asynchronous backend confirmation. Unlike a simple like button, where a single optimistic update can easily revert, a Kanban board involves multiple interdependent updates – moving items within columns and moving columns themselves. These interactions significantly increase the risk of inconsistencies, poor user experience, and even UI errors if not carefully managed.

This tutorial presents a solution: a React hook that simplifies the creation of a sortable, drag-and-drop Kanban board. We'll build a complete example, demonstrating how to handle optimistic updates effectively and maintain data consistency across the board.

Project Setup and Dependencies

We'll use these four libraries:

  • react: Our frontend library.
  • @hello-pangea/dnd: A robust drag-and-drop library. (GitHub)
  • @wazza99/use-sortable: A custom hook for managing Kanban board sorting. (NPM)
  • tailwindcss: For styling (optional; standard CSS works too).

Let's start by creating a new React project (with TypeScript) using Vite:

npm create vite@latest

Install the necessary dependencies:

npm install @hello-pangea/dnd @wazza99/use-sortable tailwindcss @tailwindcss/vite

If using Tailwind CSS, follow the Tailwind instructions for Vite integration: Tailwind CSS Documentation.

Component Structure and Data

Create three component files (Task.tsx, Column.tsx, Board.tsx) in the /src/components directory. Also, create a data.ts file in your src directory with sample data:

export default {
  columns: [
    {
      id: "column1",
      name: "Todo",
      order: 1,
      tasks: [
        {
          id: "1",
          title: "Task 1 title",
          order: 1,
        },
        {
          id: "2",
          title: "Task 2 title",
          order: 2,
        },
        {
          id: "3",
          title: "Task 3 title",
          order: 3,
        }, 
      ],
    },
    {
      id: "column2",
      name: "In Progress",
      order: 2,
      tasks: [
        {
          id: "4",
          title: "Task 4 title",
          order: 1,
        },
        {
          id: "5",
          title: "Task 5 title",
          order: 2,
        },
      // ... more tasks
      ],
    },
    // ... more columns
  ],
};

Ensure all taskId's are unique and the order within each column is ascending (this is crucial for the sorting logic!).

Building the Board Component (Board.tsx)

import { DragDropContext, Droppable } from "@hello-pangea/dnd";
import { useSortable } from "@wazza99/use-sortable";
import Column from "./Column";
import data from "../data";

const Board = () => {
  const { columns } = useSortable(data.columns, "tasks");
  return (
    <div>
      <DragDropContext onDragEnd={() => {}}>
        <Droppable
          droppableId="all-columns"
          direction="horizontal"
          type="column"
        >
          {(provided) => (
            <div
              className={`flex gap-2 px-4 my-6 w-full overflow-x-auto`}
              {...provided.droppableProps}
              ref={provided.innerRef}
            >
              {columns.map((column, index) => (
                <Column key={column.id} column={column} index={index} />
              ))}
              {provided.placeholder}
            </div>
          )}
        </Droppable>
      </DragDropContext>
    </div>
  );
};

export default Board;

This sets up the overall drag-and-drop context and makes the entire board area droppable for columns.

Creating Reusable Column and Task Components

Next, we'll create the Column.tsx and Task.tsx components to handle the column and task draggable and droppable areas, along with their visual representation:

//Column.tsx
import { Draggable, Droppable } from "@hello-pangea/dnd";
import Task from "./Task";
import { cn } from "../lib/utils";

// ... (Column component code as in the original draft) ...

//Task.tsx
import { Draggable } from "@hello-pangea/dnd";
import { cn } from "../lib/utils";

// ... (Task component code as in the original draft) ...

These components use <Draggable> and <Droppable> to enable drag-and-drop interactions and provide visual feedback.

The image in the original draft [![Image description](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fds85pi9mwns1e7i52rzb.png)](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fds85pi9mwns1e7i52rzb.png) shows the basic layout before adding sorting logic.

Implementing Sorting Functionality

To make the Kanban board sortable, we need to update the Board.tsx component to utilize the dragEndHandler function provided by @wazza99/use-sortable. This function handles the reordering logic when a drag-and-drop operation concludes. The handleDrag function below shows how to use this function:

// ... imports
import { DropResult } from "@hello-pangea/dnd";

const Board = () => {
  const { columns, dragEndHandler } = useSortable(data.columns, "tasks");

  const handleDrag = (result: DropResult) => {
    dragEndHandler(result, {
      reorderColumns: true,
      columnsDroppableId: "all-columns",
    });
  };

  return (
    // ...DragDropContext and the rest of the component code
    <DragDropContext onDragEnd={handleDrag}>
      // ...
    </DragDropContext>
  );
};

Now, the drag-and-drop operations will correctly update the order of tasks and columns.

Optimistic Updates with Asynchronous Operations

To support optimistic updates, we'll use an asynchronous function to simulate interacting with a backend API. Create this function in src/lib/utils.ts:

export function createPromise<T>(data: T, duration: number): Promise<T> {
  return new Promise((resolve) => setTimeout(() => resolve(data), duration));
}

Next, we need to update Board.tsx to add the following functions which handle optimistic updates:


// imports
import { OptimisticColumnData, OptimisticItemData, useSortable } from "@wazza99/use-sortable";
import { createPromise } from "../lib/utils";

const Board = () => {
  // ... other code ...

  async function updateTask(values: OptimisticItemData) {
    const data = await createPromise(values, 3000);
    // Do something with the data after the promise resolves
    console.log('Task updated:', data);
  }

  async function updateColumn(values: OptimisticColumnData) {
    const data = await createPromise(values, 3000);
    // Do something with the data after the promise resolves
    console.log('Column updated:', data);
  }

  const handleDrag = (result: DropResult) => {
    dragEndHandler(result, {
      reorderColumns: true,
      columnsDroppableId: "all-columns",
    }, { updateColumn: updateColumn, updateItem: updateTask });
  };

  // ... rest of the component
};

This code handles the optimistic updates using functions which are passed to the `dragEndHandler` from the @wazza99/use-sortable hook, providing smooth user feedback and resolving the backend operation in the background.

The @wazza99/use-sortable hook also provides several helpful functions such as addColumn, addColumnItem, deleteColumn, and more, detailed in its documentation: NPM Package. For more details on drag-and-drop functionality, see the @hello-pangea/dnd documentation: GitHub Repository.

Conclusion

This tutorial has demonstrated how to build a fully functional, sortable Kanban board in React with optimistic updates. By using the @wazza99/use-sortable hook and carefully handling asynchronous operations, you can create a highly responsive and user-friendly Kanban board seamlessly integrated with your backend system. This approach not only improves the user experience but also helps to maintain data consistency and prevents common issues associated with optimistic updates in complex UI components.

Hashtags: #React # Kanban # DragAndDrop # Sortable # OptimisticUpdates # BackendIntegration # TypeScript # UseSortable # HelloPangeaDnd # TailwindCSS # Vite

Related Articles

thumb_nail_Unveiling the Haiku License: A Fair Code Revolution

Software Development

Unveiling the Haiku License: A Fair Code Revolution

Dive into the innovative Haiku License, a game-changer in open-source licensing that balances open access with fair compensation for developers. Learn about its features, challenges, and potential to reshape the software development landscape. Explore now!

Read More
thumb_nail_Leetcode - 1. Two Sum

Software Development

Leetcode - 1. Two Sum

Master LeetCode's Two Sum problem! Learn two efficient JavaScript solutions: the optimal hash map approach and a practical two-pointer technique. Improve your coding skills today!

Read More
thumb_nail_The Future of Digital Credentials in 2025: Trends, Challenges, and Opportunities

Business, Software Development

The Future of Digital Credentials in 2025: Trends, Challenges, and Opportunities

Digital credentials are transforming industries in 2025! Learn about blockchain's role, industry adoption trends, privacy enhancements, and the challenges and opportunities shaping this exciting field. Discover how AI and emerging technologies are revolutionizing identity verification and workforce management. Explore the future of digital credentials today!

Read More
Your Job, Your Community
logo
© All rights reserved 2024