DEV Community 👩‍💻👨‍💻

Cover image for Getting started with React Dnd-Kit
shubhadip
shubhadip

Posted on

Getting started with React Dnd-Kit

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>
  );
}
...
Enter fullscreen mode Exit fullscreen mode

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>
...

Enter fullscreen mode Exit fullscreen mode

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);
      });
    }
  }
Enter fullscreen mode Exit fullscreen mode

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>)
  }
  ...
Enter fullscreen mode Exit fullscreen mode

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,
    })
  );
Enter fullscreen mode Exit fullscreen mode

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,
    })
  );
Enter fullscreen mode Exit fullscreen mode

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 (0)

Classic DEV Post from 2020:

js visualized

🚀⚙️ JavaScript Visualized: the JavaScript Engine

As JavaScript devs, we usually don't have to deal with compilers ourselves. However, it's definitely good to know the basics of the JavaScript engine and see how it handles our human-friendly JS code, and turns it into something machines understand! 🥳

Happy coding!