Как добавить перетаскивание (Drag and Drop) в React

Веб‑интерфейсы часто выигрывают от возможности перетаскивать элементы: пользователи могут менять порядок, перемещать карточки между колонками, редактировать расположение визуально. В React есть два основных подхода:
- нативные возможности браузера (HTML5 drag-and-drop API),
- сторонние библиотеки, которые накрывают детали и добавляют удобства.
Ниже — упорядоченное руководство: что выбрать, как реализовать, как сделать доступно и тестируемо.
Почему важен выбор подхода
Коротко: нативный API прост для одиночных случаев, но быстро становится громоздким при сложных интерфейсах (мульти‑колонки, зеркалирование элементов, плавные анимации, мобильная поддержка и доступность). Библиотеки берут на себя работу по синхронизации позиции, коллизиям и анимациям, но добавляют зависимость и кривую обучения.
Важно: не стоит автоматически выбирать библиотеку для простых задач. Оцените сложность, требования к доступности и бюджет на поддержку.
Основные события natively (коротко)
Определения в одну строку:
- onDragStart — начало перетаскивания; здесь обычно кладут данные в dataTransfer.
- onDrag — повторяющееся событие во время перетаскивания.
- onDragOver — событие при наведении на потенциальную цель; по умолчанию запрещает drop, поэтому обычно вызывают event.preventDefault().
- onDrop — момент отпускания; здесь читают данные и обновляют состояние.
- onDragEnd — окончание операции, используется для очистки временных данных.
Ключевые детали
- Для того чтобы элемент был draggable, у него должен быть атрибут draggable (true по умолчанию для изображений, иначе явно). В JSX можно просто написать draggable.
- Чтобы drop сработал, целевой контейнер должен отменять поведение по умолчанию в onDragOver: event.preventDefault().
- Для передачи данных между элементами используется event.dataTransfer.setData/getData (обычно MIME ‘text/plain’ или собственный ключ).
Пример 1. Нативное перетаскивание (из исходника)
Ниже — код из примера (сохранён как есть). Он показывает базовые обработчики событий на классовом компоненте.
import React, { Component } from 'react';
class MyComponent extends Component {
render() {
return (
Drag me!
);
}
}
export default MyComponent;И пример методов:
import React, { Component } from 'react';
class MyComponent extends Component {
handleDragStart(event) {
// This method runs when the dragging starts
console.log("Started")
}
handleDrag(event) {
// This method runs when the component is being dragged
console.log("Dragging...")
}
handleDragEnd(event) {
// This method runs when the dragging stops
console.log("Ended")
}
render() {
return (
Drag me!
);
}
}
export default MyComponent;Обратите внимание: в примерах событие onDragOver на приёмнике не показано — без него drop не произойдёт.
Перемещение элемента по координатам
Пример с хуком useState из исходника показывает, как переместить блок по координатам клиента после окончания перетаскивания:
import React, { Component, useState } from 'react';
function MyComponent() {
const [x, setX] = useState(0);
const [y, setY] = useState(0);
const handleDragEnd = (event) => {
setX(event.clientX);
setY(event.clientY);
};
return (
Drag me!
);
}
export default MyComponent;Это простая техника, но имейте в виду: event.clientX/clientY дают координаты указателя, а не смещения элемента относительно контейнера, поэтому для корректного позиционирования часто нужен учёт смещения контейнера и scroll.
Пример 2. Использование сторонней библиотеки (из исходника)
Библиотека из примера — react-drag-and-drop. Пример использования:
import React, { Component } from 'react';
import { Draggable, Droppable } from 'react-drag-and-drop';
class MyComponent extends Component {
render() {
return (
Drag me!
Drop here!
);
}
handleDrop(data, event) {
// This method runs when the data drops
console.log(data); // 'bar'
}
}
export default MyComponent;Такие обёртки упрощают API и часто добавляют свой внутренний слой для управления состоянием и анимациями.
Советы по созданию удобных DnD-компонентов
- Визуальная обратная связь. Меняйте opacity, добавляйте тень, рамку или «флоринг» (placeholder), чтобы пользователь видел, что элемент выбран.
- Явно разрешайте drop с помощью onDragOver + event.preventDefault().
- Ограничьте допустимые группы переносимых данных (type/props или MIME) — это предотвратит некорректные операции.
- Дайте возможность отменить действие (Esc, кнопка «отменить», отмена drag по потере фокуса).
- Поддержка мобильных: HTML5 DnD плохо работает на мобильных браузерах — используйте pointer/touch события или библиотеки с мобильной поддержкой.
- Тестируйте на производительность при большом количестве элементов — дебаунс событий и отрисовки может понадобиться.
Когда нативный подход не подходит
- если вам нужны плавные перестановки с анимациями (когда карточка перемещается и остальные сдвигаются плавно),
- если требуется сложная логика сортировки между несколькими контейнерами,
- когда нужна полноценная поддержка клавиатуры и экранных читалок без дополнительной доработки.
В таких случаях рассмотрите библиотеку.
Популярные библиотеки: плюсы и минусы
- react-beautiful-dnd — удобна для упорядочения списков и колонок, отличные анимации; авторская библиотека Atlassian, но сейчас в состоянии кода нужно проверять совместимость с новыми версиями React.
- react-dnd — гибкая, основана на концепции провайдеров и backend’ов (HTML5, touch); требует большего объёма кода, но даёт контроль.
- dnd-kit — современная, модульная, с хорошей мобильной поддержкой и производительностью; активно развивается.
- react-drag-and-drop (как в исходнике) — простая обёртка, подойдёт для базовых сценариев.
Матрица сравнения (качественная):
| Задача | Нативный API | react-beautiful-dnd | react-dnd | dnd-kit |
|---|---|---|---|---|
| Быстрая простая перетаскиваемость | ✓ | ✗ | ✗ | ✗ |
| Сортировка списков с анимацией | ✗ | ✓ | ✓ (сложно) | ✓ |
| Мобильная поддержка | ✗ | ограничена | требует backend | ✓ |
| Доступность (ARIA/клавиатура) | вручную | встроена частично | вручную | хорошие примеры |
(Матрица качественная, проверяйте версии библиотек для совместимости с вашим стеком.)
Мини‑методология: как встроить DnD в проект (шаги)
- Определите требования: сортировка, мульти‑колонки, мобильность, доступность.
- Сделайте прототип на нативном API для оценки объёма работы.
- Если нужна сложность — выберите библиотеку и выполните PoC (proof of concept) для ключевых пользовательских сценариев.
- Реализуйте визуальную обратную связь и фоллбеки для устройств без поддержки drag.
- Добавьте тесты (юнит + интеграция) и проверку клавиатуры/экранных читалок.
- Замерьте производительность и оптимизируйте рендеринг (memo, virtualization для длинных списков).
Пример: упорядочивание списка с помощью natively (практическая реализация)
Ниже — упрощённый шаблон на хуках, реализующий перестановку списка через dataTransfer:
// Псевдокод/шаблон: суть передачи индекса через dataTransfer
function ListItem({ item, index, onDropAt }) {
function handleDragStart(e) {
e.dataTransfer.setData('text/plain', String(index));
}
function handleDrop(e) {
e.preventDefault();
const from = Number(e.dataTransfer.getData('text/plain'));
onDropAt(from, index);
}
return (
e.preventDefault()}>
{item.text}
);
}В коде компонент List держит массив и корректно переставляет элементы по индексам. Это простая и понятная реализация для небольших списков.
Доступность (A11y)
Короткие конкретные рекомендации:
- Обеспечьте управление клавиатурой: кнопки перемещения (Up/Down), ловите Enter/Space для взятия/отпускания элемента.
- Используйте aria-grabbed, role=”list” и role=”listitem” там, где уместно.
- Сообщайте изменения через live region (aria-live) при окончании перемещения.
- Не полагайтесь только на цветовую индикацию — добавляйте текстовые или геометрические подсказки.
Пример: при реализации сортировки добавьте небольшие «переместить вверх/вниз» кнопки для пользователей клавиатуры.
Тестирование и критерии приёмки
Критерии приёмки:
- Элемент можно перетащить и отпустить в допустимые цели.
- Перезапись состояния происходит корректно (проверка порядка элементов).
- Операция отменяема (Esc или кнопка «Отмена»).
- Визуальная обратная связь присутствует: перетаскиваемый элемент легко идентифицировать.
- Клавиатурная навигация позволяет выполнить те же операции.
Тесты:
- Юнит: имитировать события onDragStart/onDrop и проверить изменение состояния.
- Интеграция: end‑to‑end тест с Cypress/Playwright — проверка поведения в реальном браузере.
- Accessibility: автоматический аудит (axe) + ручная проверка клавиатуры.
Чек‑лист по ролям
Для разработчика:
- Есть PoC для ключевых сценариев.
- Реализована отмена и обработка ошибок.
- Добавлены unit и e2e тесты.
Для дизайнера:
- Даны состояния перетаскивания (hover, dragging, drop target).
- Есть фоллбеки для цветовой слепоты и малого размера шрифта.
Для продуктового менеджера:
- Оценено влияние на мобильных пользователях.
- Согласовано поведение при конфликтных действиях (несколько пользователей).
Риски и смягчения
- Риск: плохая мобильная поддержка — Смягчение: использовать библиотеку с pointer/touch поддержкой (dnd-kit) или реализовать touch fallback.
- Риск: проблемы с доступностью — Смягчение: добавить клавиатурные контролы и тесты с screen reader.
- Риск: производительность при 100+ элементах — Смягчение: виртуализировать список, ограничить частоту обновлений.
Когда выбирать библиотеку
Выбирайте библиотеку, если хотя бы одно из условий истинно:
- Нужна сложная перестановка с анимациями.
- Требуется поддержка drag между несколькими списками/колонками.
- Нужно готовое решение для доступности.
Короткая сводка
- Начинайте с оценки требований и PoC.
- Нативный API хорош для простых случаев, но требует ручной доработки для доступа, мобильности и анимаций.
- Библиотеки ускоряют разработку сложных интерфейсов, но добавляют зависимость и могут требовать миграции при обновлениях.
- Обязательно тестируйте поведение клавиатурой и на мобильных устройствах.
Важно: перетаскивание — мощный UX‑инструмент, но он должен быть предсказуемым, доступным и отзывчивым.
Краткое резюме
- Перетаскивание в React можно сделать нативно или с библиотекой.
- Нативный подход прост для одиночных элементов.
- Для сложных сценариев используйте react-beautiful-dnd, react-dnd или dnd-kit.
- Не забывайте про доступность, мобильные устройства и тесты.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone