How I implement sortable list with dndkit in React
After push the Alpha launching on my latest product Pithy Point, I realize that it’s lack of re-ordering slides function. This is crucial, because changing slide order is a very common behaviour when working with slide. So I have to somehow add this function ASAP.
Sortable list with drag and drop
Above is a demonstration of what is sortable list with drag and drop. Quick research on internet has lead me to dnd kit and some other packages, but this one is one of the most downloaded, so let’s use it.
For a very common list in React, you will have something like this
function List() {
return (
<div>
{listData.map((item) => {
return <Item key={item.id} value={item.value} />;
})}
</div>
);
}
function Item({ value }) {
return <div className="item">{value}</div>;
}
1. Install the package
Install dndkit core package and some other plugin packages. For a least demand to have sortable list, you will need
npm install --save @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities
2. Wrap your List with DndContext and SortableContext
import { DndContext } from "@dnd-kit/core";
import { SortableContext } from "@dnd-kit/sortable";
function List() {
return (
<DndContext>
<SortableContext items={listData.map((e) => e.id)}>
<div>
{listData.map((item) => {
return <Item key={item.id} value={item.value} />;
})}
</div>
</SortableContext>
</DndContext>
);
}
DndContext
here to provide the higher level context about how dns handling the dragging behavior.
SortableContext
to provide the specific sorting context, such the list of data.
3. Update your Item so thatit can be dragged
import { useSortable } from "@dnd-kit/sortable";
function Item({ value }) {
const { setNodeRef, attributes, listeners, transform, transition } =
useSortable({
id: value,
});
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
return (
<div
ref={setNodeRef}
style={style}
{...attributes}
{...listeners}
className="item"
>
<div>{value}</div>
</div>
);
}
useSortable
here to provide drag and drop abilities to your item elemenet. Remember to use unique id for each item because it might produce weirdo-behavior if there’s duplicated ID
4. Add dragEnd handler to sync your list state with the list in dns
import { arrayMove, SortableContext } from "@dnd-kit/sortable";
import {
DndContext,
DragEndEvent,
PointerSensor,
} from "@dnd-kit/core";
function List() {
function onDragEnd(event: DragEndEvent) {
const { active, over } = event;
const activeId = active.id; // the item you're dragging
const overId = over?.id; // the item you place current item over. This could be same as dragging item, which mean you didn't move it
if (activeId === overId) return;
const activeColumnIndex = listData.findIndex((col) => col.id === activeId);
const overColumnIndex = listData.findIndex((col) => col.id === overId);
const newArray = arrayMove(listData, activeColumnIndex, overColumnIndex);
setListData(newArray);
}
return (
<DndContext onDragEnd={onDragEnd}>
<SortableContext items={listData.map((e) => e.id)}>
<div>
{listData.map((item) => {
return <Item key={item.id} value={item} />;
})}
</div>
</SortableContext>
</DndContext>
);
}
arrayMove
here produce a new array with matching position as what you see in UI. This’s just a handy function so if you wish to implement the repositioning yourself, feel free to do that.
5. (Optional) Resolve conflict with onClick event you may have on your Item
import {
DndContext,
DragEndEvent,
PointerSensor,
useSensor,
useSensors,
} from "@dnd-kit/core";
import { arrayMove, SortableContext } from "@dnd-kit/sortable";
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
distance: 8,
},
}),
);
function List() {
return (
<DndContext sensors={sensors} onDragEnd={onDragEnd}>
{/* rest of the code */}
</DndContext>
);
}
If you have the existed click event on the Item like me, your click handler may not work anymore due to override by the listender
from useSortable
. To resolve this, you can use useSensor
to separate your click event from the drag and drop.
That’s all, now you have a minimal sortable list with drag and drop functionality in React using DND Kit. You can further customize it by adding more features like sorting, filtering, etc. Happy coding! 🚀
Referrences:
Thanks for reading