How to stop / minimize sortableItem re-renders during drag events with dndkit

170 Views Asked by At

I was playing around with the dndkit library, trying to make a simple sortable list with drag and drop and I am a bit worried with the potential poor performance of the app in the long run.

I have been having trouble with stopping or at least minimizing re-renders of the sortable items when dragging (starting the drag, moving over other items and dropping). Looks like whenever you start the drag event and move the item, the item component gets re-rendered, so even a simple drag animation causes hundreds of re-renders for all the items in the list. I am not sure whether the animation is what causes the re-renders but I would really like to keep it, while optimizing the renders.

Here is my code snippet that illustrates the issue

import { useState } from "react";
import { DndContext, closestCenter } from "@dnd-kit/core";
import {
  arrayMove,
  SortableContext,
  verticalListSortingStrategy,
  useSortable,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";

export function App() {
  const [items, setItems] = useState(["Item1", "Item2", "Item3"]);

  const handleDragEnd = (event) => {
    const { active, over } = event;

    if (active.id !== over.id) {
      setItems((items) => {
        const activeIndex = items.indexOf(active.id);
        const overIndex = items.indexOf(over.id);
        return arrayMove(items, activeIndex, overIndex);
      });
    }
  };

  return (
    <DndContext collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
      <div className="p-3">
        <SortableContext
          items={items}
          strategy={verticalListSortingStrategy}
        >
          {items.map((item, index) => (
            <SortableItem key={item} id={item} index={index} />
          ))}
        </SortableContext>
      </div>
    </DndContext>
  );
}

const SortableItem = (props) => {
  const { attributes, listeners, setNodeRef, transform, transition } =
    useSortable({ id: props.id });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
  };

  console.log(`renderedItem: ${props.index}`);

  return (
    <div
      className="border border-red-500 bg-yellow-50"
      ref={setNodeRef}
      style={style}
      {...attributes}
      {...listeners}
    >
      <div className="p-3">{props.id}</div>
    </div>
  );
};


I have tried using useMemo and memo for the components and items but that didn't help at all. Also I have found this suggestion on the package repo issues: https://github.com/clauderic/dnd-kit/issues/994

I have tried using a "wrapper component" for the sortable items but that only helps with optimizing the item cards, the wrapper component still gets re-rendered each time (I am not sure whether that helps the performance).

I have previously used the react-beautiful-dnd library which has a beautiful drag overlay animation when holding the draggable item and it somehow managed to not cause re-renders, only re-rendering the items when the dropped (changes sorting). Unfortunately it's also limited, best suited for only vertical and horizontal sortings (specifically for kanban boards) and looks like there won't be future maintenance. Dndkit offers similar beautiful animation style when sorting and larger versatility for other use cases but the performance issue is a bit concerning.

Is there anyone who had success optimizing it? How did you do it and is it even worth the trouble?

0

There are 0 best solutions below