Хорошо, потребовалось время, чтобы по-настоящему разобраться в этом, и лучшее, что я мог придумать, - это когда вы перетаскиваете элемент, который вы продвигали, и обновляете состояние, но если пользователь отказывается от перетаскивания, элемент "возвращается" обратно в откуда он возник, и не последним, где он завис. Я потратил некоторое время на изучение Drag and Drop API , и ничего особо не выскочило, как возможность изменить это поведение «в полете».
То, что я считаю технически более правильное поведение для брошенных отбрасываний, т. Е. Пользователь не отбрасывает элемент в списке, состоит в том, чтобы список сохранял состояние, в котором он находился ранее с до перетаскивания.
Вот решение, которое добавляет дополнительную часть состояния для изменения во время события перетаскивания, и если перетаскивание прошло успешно, то временное состояние фиксируется в «реальном» состоянии списка, а в случае неудачи возвращается обратно в существующее состояние списка (оно просто остается неизменным).
import React, { useState } from "react";
import "./App.css";
const initList = [1, 2, 3, 4, 5, 6, 7, 8, 9];
function App() {
const [list, setList] = useState(initList);
const [dragList, setDragList] = useState(null);
const [draggedItem, setDraggedItem] = useState(null);
/**
* On drag start
* Initialize the temporary item being dragged and drag list.
*/
const onDragStartHandle = index => e => {
setDraggedItem(list[index]);
setDragList(list);
e.dataTransfer.effectAllowed = "move";
e.dataTransfer.setData("text/html", e.target.parentNode);
e.dataTransfer.setDragImage(e.target.parentNode, 20, 22);
};
/**
* On drag end
* Save updated drag list to list and nullify temporary values.
*/
const onDragEndHandle = e => {
e.preventDefault();
setList(dragList);
setDragList(null);
setDraggedItem(null);
};
/**
* On drag leave
* Item wasn't dropped so reset temporary drag list
*/
const onDragLeaveHandle = e => {
e.preventDefault();
setDragList(list);
};
/**
* On drag over
* If dragging over another item update the drag list with new position
*/
const onDragOverHandle = index => e => {
e.preventDefault();
const draggedOverItem = dragList[index];
if (draggedOverItem === draggedItem) {
return;
}
const items = dragList.filter(item => item !== draggedItem);
items.splice(index, 0, draggedItem);
setDragList(items);
};
return (
<div className="App">
<header className="App-header">
<h3>Drag'n'Drop</h3>
<ul>
{(draggedItem ? dragList : list).map((item, index) => (
<li key={index} className="item-style">
<div
draggable
onDragOver={onDragOverHandle(index)} // Moved *
onDragLeave={onDragLeaveHandle}
onDragEnd={onDragEndHandle}
onDragStart={onDragStartHandle(index)}
>
{item}
</div>
</li>
))}
</ul>
</header>
</div>
);
}
export default App;
* Перемещено из элемента li
в div
, чтобы все события происходили из одного элемента. Наличие прослушивателя событий onDragOver
в элементе списка вызывало странную проблему, из-за которой пользовательский интерфейс иногда «застревал» в состоянии полу-зависания.
Редактировать список перетаскивания