useDrag для перетаскиваемых элементов

Хук useDrag предоставляет механизм для создания доступных перетаскиваемых элементов в приложениях на React. Он абстрагирует низкоуровневую работу с событиями мыши, сенсорными экранами и клавиатурой, обеспечивая совместимость с ARIA-стандартами.

Импорт и базовое использование

import {useDrag} from '@react-aria/dnd';
import {useRef} from 'react';

function DraggableItem({item}) {
  let ref = useRef();
  let {dragProps} = useDrag({
    id: item.id,
    type: 'item',
  });

  return (
    <div ref={ref} {...dragProps}>
      {item.name}
    </div>
  );
}
  • id — уникальный идентификатор перетаскиваемого элемента. Используется для отслеживания состояния перетаскивания.
  • type — тип элемента, важен при работе с useDrop для фильтрации совместимых целей.
  • dragProps — набор пропсов, которые необходимо распространить на корневой элемент для корректной работы перетаскивания.

Работа с состоянием перетаскивания

Хук возвращает объект с основными свойствами:

  • isDragging — булевое значение, указывающее, перетаскивается ли элемент в данный момент.
  • dragProps — обработчики событий и атрибуты ARIA.

Пример использования состояния для визуальной индикации:

function DraggableItem({item}) {
  let ref = useRef();
  let {dragProps, isDragging} = useDrag({
    id: item.id,
    type: 'item',
  });

  return (
    <div
      ref={ref}
      {...dragProps}
      style={{
        opacity: isDragging ? 0.5 : 1,
        cursor: 'grab',
      }}
    >
      {item.name}
    </div>
  );
}

Настройка сенсорного и клавиатурного взаимодействия

useDrag автоматически поддерживает:

  • Клавиатурное управление: стрелки, пробел и Enter могут инициировать перетаскивание.
  • Сенсорные устройства: поддержка касаний и жестов “drag-and-drop”.

Можно переопределить поведение с помощью колбэков:

  • onDragStart — вызывается при начале перетаскивания.
  • onDragEnd — вызывается после завершения перетаскивания.
  • onDragMove — срабатывает при каждом перемещении элемента.

Пример расширенного управления:

let {dragProps, isDragging} = useDrag({
  id: item.id,
  type: 'item',
  onDragStart: () => console.log('Начало перетаскивания', item.id),
  onDragMove: (position) => console.log('Текущая позиция', position),
  onDragEnd: (result) => console.log('Завершено', result),
});

Совмещение с useDrop

Перетаскиваемый элемент в паре с зоной сброса (useDrop) позволяет реализовать полноценные списки, сетки и канбан-доски. Важно указывать совместимый type, чтобы ограничить допустимые цели.

Пример зоны сброса:

import {useDrop} from '@react-aria/dnd';

function DropZone({onDrop}) {
  let ref = useRef();
  let {dropProps, isDropTarget} = useDrop({
    accept: 'item',
    onDrop: (draggedItem) => onDrop(draggedItem),
  });

  return (
    <div
      ref={ref}
      {...dropProps}
      style={{
        background: isDropTarget ? 'lightblue' : 'transparent',
        minHeight: '100px',
        border: '1px dashed gray',
      }}
    >
      Перетащите сюда элемент
    </div>
  );
}

Полезные практики

  1. Использовать ref на корневом элементе — React Aria требует прямой ссылки на DOM-узел.
  2. Обеспечивать доступностьuseDrag автоматически добавляет атрибуты aria-grabbed и роль button/listitem.
  3. Стилизация состояния перетаскивания — визуально выделять активный элемент с помощью opacity, cursor или теней.
  4. Разделять логику и отображение — лучше держать хук useDrag отдельно от визуальных компонентов для тестируемости.
  5. Обрабатывать множественные устройства ввода — хук уже учитывает мышь, клавиатуру и сенсорные экраны, но кастомизация через колбэки позволяет адаптировать поведение под конкретные UX-требования.

Интеграция с контекстом

Для больших приложений можно обернуть список перетаскиваемых элементов в контекст Drag-and-Drop, чтобы управлять состоянием глобально:

import {DragDropProvider} from '@react-aria/dnd';

function App() {
  return (
    <DragDropProvider>
      <DraggableList items={items} />
    </DragDropProvider>
  );
}

Это позволяет отслеживать активный элемент, блокировать одновременное перетаскивание нескольких объектов и упрощает реализацию комплексных интерфейсов, таких как доски задач или редакторы списков.

Особенности и ограничения

  • useDrag не управляет позицией элемента на экране. Для этого требуется отдельная логика или интеграция с библиотекой визуальной анимации (framer-motion, react-spring).
  • Необходимо следить за уникальностью id для корректной работы событий onDragEnd.
  • Поддержка клавиатуры добавляет дополнительную доступность, но может требовать кастомизации для сложных интерфейсов с вложенными списками.

useDrag предоставляет мощный, полностью доступный способ реализовать перетаскивание элементов в React-приложениях, упрощая работу с различными устройствами ввода и обеспечивая совместимость с ARIA-стандартами.