📍참고
https://www.aurigait.com/blog/drag-and-drop-in-react-without-using-any-external-library/
Drag and Drop in React without using any External Library - Auriga IT
Drag and Drop feature in React with little effort using useRef hook. Here we'll see it in action by implementing an easy drag and drop list.
www.aurigait.com
https://developer.mozilla.org/ko/docs/Web/API/HTML_Drag_and_Drop_API
HTML 드래그 앤 드롭 API - Web API | MDN
HTML 드래그 앤 드롭 인터페이스는 파이어폭스와 다른 브라우저에서 어플리케이션이 드래그 앤 드롭 기능을 사용하게 해줍니다. 이 기능을 이용해 사용자는 draggable 요소를 마우스로 선택해 droppa
developer.mozilla.org
- HTML Drag and Drop API
📍1. 리스트 생성 및 원소들에 draggable 어트리뷰트 부여
- element에 draggable을 부여하기만 해도 드래그 효과가 가능
📍2. useRef 훅으로 드래그되는 아이템 추적하기
- useRef 훅으로 드래그될 아이템의 리스트 내 인덱스를 기억
- 드래그 시작 이벤트에 반응하는 onDragStart 이벤트 리스너를 draggable 요소에 부착
📍3. 드랍될 위치의 아이템추적하기
- useRef 훅으로 드랍될 위치(드래그중인 요소가 침범한 기존 위치)의 인덱스를 기억
- onDragEnter 이벤트 리스너를 draggable 요소에 부착
- onDragEnter 이벤트 리스너 : 드래그중인 커서가 들어올 때 (이벤트 리스너가 부착된 요소 안으로) 발동
- 현재 드래그중인 요소에도 onDragEnter 이벤트 리스너가 부착되어 있음
📍4. draggable element들의 리스트 순서 재배열
- onDragEnd 이벤트리스너는 드래그 이벤트가 끝나면 발동
- splice() 함수를 이용해 드래그 시작 인덱스의 원소를 제거 (deleteCount = 1)
- 그리고 splice 함수로 dragEnter된 인덱스에 useRef로 저장한 드래그 시작 인덱스를 추가 (deleteCount = 0)
이 때, deleteCount가 0이므로, 기존 요소가 제거되지 않고 새로운 원소에 의해 한 칸 밀림
📍5. drag over 애니메이션 효과 제거
- 커서의 금지 효과를 제거하기 위해 onDragOver 이벤트 리스너에 event.preventDefault() 를 추가해 준다
📍완성된 코드
https://codesandbox.io/s/dnd-k9wvtw?file=/src/App.js
dnd - CodeSandbox
dnd by jong-k using react, react-dom, react-scripts
codesandbox.io
import React, { useState, useRef } from "react";
import "./styles.css";
const App = () => {
const dragItem = useRef(); // 드래그할 아이템의 인덱스
const dragOverItem = useRef(); // 드랍할 위치의 아이템의 인덱스
const [list, setList] = useState([
"Item 1",
"Item 2",
"Item 3",
"Item 4",
"Item 5",
"Item 6"
]);
// 드래그 시작될 때 실행
const dragStart = (e, position) => {
dragItem.current = position;
console.log(e.target.innerHTML);
};
// 드래그중인 대상이 위로 포개졌을 때
const dragEnter = (e, position) => {
dragOverItem.current = position;
console.log(e.target.innerHTML);
};
// 드랍 (커서 뗐을 때)
const drop = (e) => {
const newList = [...list];
const dragItemValue = newList[dragItem.current];
newList.splice(dragItem.current, 1);
newList.splice(dragOverItem.current, 0, dragItemValue);
dragItem.current = null;
dragOverItem.current = null;
setList(newList);
};
return (
<>
{list &&
list.map((item, idx) => (
<div
key={idx}
style={{
backgroundColor: "lightblue",
margin: "20px 25%",
textAlign: "center",
fontSize: "40px"
}}
draggable
onDragStart={(e) => dragStart(e, idx)}
onDragEnter={(e) => dragEnter(e, idx)}
onDragEnd={drop}
onDragOver={(e) => e.preventDefault()}
>
{item}
</div>
))}
</>
);
};
export default App;