In this article we are going to use one of the drag n drop library for react. There are few good drag n drop libraries for react like react-dnd, dnd-kit & react-beautiful-dnd.
We are going to look into dnd-kit today reason behind using this library was that it supports a lot of usecase, availability of hooks, being the light-weight etc.
To start with lets create an react app with create-react-app & install the necessary libraries with it
npx create-react-app react-dndkit-eg
npm install --save @dnd-kit/core @dnd-kit/sortable
@dntkit/core & @dndkit/sortable these are the two libraries which we will be requiring to support basic dragndrop functionalities, library also provides support various other features with other libs like @dnd-kit/utilities
, @dnd-kit/modifiers
, @dnd-kit/accessibility
more details about each of these can be read on their website.
To start with we will be creating component which will be wrapping our draggable/sortable component, the whole idea of dnd in simple terms it to have a container where your item can be dragged into or moved across so the code regarding the component will somewhat look like this sortable-component-complete-code
...
# e.g code snippet
export function SortableItem(props) {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
} = useSortable({id: props.id});
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
if(props.handle) {
return (<div ref={setNodeRef} style={style} {...attributes} className={'pos-relative'}>
{
props.handle ?
<span className='dragHandleClass' {...listeners}>
# some svg/img/htmlelement
</span>
: null
}
{props.children}
</div>)
}
return (
<div ref={setNodeRef} style={style} {...attributes} {...listeners} className={'pos-relative'}>
{props.children}
</div>
);
}
...
the above code acts as wrapper for the component that needs to dragged/sorted, we will talk about the handle prop later in the article, the id
prop is passed to useSortable hook so that every item can be uniquely identified.
Now lets create a component which will be having multiple items that can be sorted or dragged on, to create a container we would need DndContext & SortableContext so that the grid & row elements can be moved/sorted across.
DndContext takes a fews props some of them are sensors, collisionDetection etc these also includes functions like handleDragStart & handleDragEnd these are the functions which can be use before & after the whole dragndrop interaction.
Similarly SortableContext takes few props , here we will have to pass items which should be an array on uniqueIds & this should be same as that we have passed to sortableItem above.
The code of the context should be similar to this sortable-context-complete-code
...
# e.g sortable context/container
<DndContext
id={'grid-dnd-basic'}
onDragEnd={handleDragEnd}
sensors={sensors}
collisionDetection={closestCenter}
>
<SortableContext
id={'grid-sort-contextbasic'}
items={items.map((i) => i?.id)}
>
{items.map(value => {
return (
<SortableItem handle={true} key={value?.id} id={value?.id} >
# sortableItem Content
</SortableItem>
)
})}
</SortableContext>
</DndContext>
...
At this point we are done with our component setup regarding drag n drop, now we will have to add handler to functions like handleDragStart/handleDragEnd, code for these are almost similar to what the documentation of dnd-kit provides only change is the items property that is passed to the handler function
function handleDragEnd(event) {
const {active, over} = event;
if (active.id !== over.id) {
setItems((items) => {
const oldIndex = (items.map(i => i.id)).indexOf(active.id);
const newIndex = (items.map(i => i.id)).indexOf(over.id);
return arrayMove(items, oldIndex, newIndex);
});
}
}
in the above code you can see we are using map function to pass only ids for the indexOf function as everything is mapped to uniqueId that is passed to sortableItem & sortableContext.
So we are almost ready with our dnd-kit implementation for sorting using dndContext & sortableContext.
Now lets visit the handle prop that we have used earlier in our sortableItem, so we can see useSortable provides a listeners now if we want to drag the item using some handler and not the actual item then we can use handle prop to apply listener to the drag-handler directly, in this way we will be able to drag via some handler and not he actual item
# e.g handle implementation
...
if(props.handle) {
return (<div ref={setNodeRef} style={style} {...attributes} className={'pos-relative'}>
{
props.handle ?
<span className='dragHandleClass' {...listeners}>
# svg/image/html-element
</span>
: null
}
{props.children}
</div>)
}
...
Now lets talk about the sensors in the whole e.g we will be using the basic sensor implementation in the doc which looks similar to this
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
);
Now these use sensor takes second parameters as object which also has a property activationConstraint
, now this can be used to activate the sensor only after some pixel movements code.
const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
);
The scenarios where this can be used is when you have an click listener to you sortableItem & you don't use drag-handle, so that we can uniquely identify click & drag events, without this constraint event click will be treated as drag events & our drag-handler functions like onDragEnd & onDragStart will be triggered.
this article mostly covers topics related to dndContext and sortable preset more about the same can be read at dnd-kit website.
The code related to the article is present in the GithubLink & gh-pages
Top comments (1)
Thank you so much