현재 ToDoBoxList안에 ToDoBox가 있는 형태이다.
ToDoBoxList 내에서 ToDoBox를 drag&drop하여 순서를 변경해보려고 한다.
사용한 라이브러리: react-dnd
https://react-dnd.github.io/react-dnd/about
제공되는 간단한 예시 codesandbox
https://codesandbox.io/s/github/react-dnd/react-dnd/tree/gh-pages/examples_hooks_js/04-sortable/cancel-on-drop-outside?from-embed=&file=/src/Card.jsx
드래그하여 옮기려는 컴포넌트(ToDoBox.js) 코드를 처음에는 다음과 같이 작성하였다.
const [{ isDragging }, dragRef, previewRef] = useDrag(() => ({
// isDragging은 드래그 되는 중인지, dragRef는 선택시 드래그되는 영역, previewRef는 드래그시 보여지는 preview
type: ItemTypes.ToDoBox, // example 코드샌드박스에서처럼 따로 파일을 생성하여 명시했다.
item: { id, index }, // 드래그되는 아이템에 들어가는 정보
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
end: (item, monitor) => { // 드래그가 끝났을 때 실행
const { id: originId, index: originIndex } = item;
const didDrop = monitor.didDrop();
if (!didDrop) { // dropRef가 연결되어있는 태그에 drop되지 않았을 경우엔
moveToDoBox(originId, originIndex, false); // 원래 자리로 되돌려놓는다.
}
},
}), [id, index, moveToDoBox]);
const [, dropRef] = useDrop({
accept: ItemTypes.ToDoBox,
hover: ({ id: draggedId, index: orgIndex }) => {
if(draggedId !== id) // hover되는 box의 id와 드래그 되는 box의 id가 다르면 해당 자리로 box를 옮긴다.
moveToDoBox(draggedId, index, true);
}
})
```
하지만 hover 될때 component들이 너무 빨리 업데이트 되어 DnD가 따라잡지 못하는 문제가 발생하여
'react index.ts:28 Uncaught Invariant Violation: Expected to find a valid target. targetId=T2' 와 같은 오류가 발생했다.
이를 해결하기 위해 lodash에서 _를 import하고 hover를 처리하기 위해 debounce를 추가하였다.
참고: https://stackoverflow.com/questions/64894806/expected-to-find-a-valid-target-react-dnd
hover 해결 코드(ToDoBox.js)
```javascript
const [{ isDragging }, dragRef, previewRef] = useDrag(() => ({
type: ItemTypes.ToDoBox,
item: { id, index },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
end: (item, monitor) => {
const { id: originId, index: originIndex } = item;
const didDrop = monitor.didDrop();
if (!didDrop) {
moveToDoBox(originId, originIndex, false);
}
},
}), [id, index, moveToDoBox]);
const debounceHoverItem = _.debounce((draggedId, index, shouldCallApi)=> {
if(draggedId === id) {
return null;
}
moveToDoBox(draggedId, index, shouldCallApi)
}, 70);
const [, dropRef] = useDrop({
accept: ItemTypes.ToDoBox,
hover: ({ id: draggedId, index: orgIndex }) => {
debounceHoverItem(draggedId, index, true);
}
})
```
ToDoBoxList에서는 ToDoBox로 moveToDoBox라는 함수를 넘겨준다.
ToDoBoxList 내에서 옮기려는 ToDoBox의 순서를 변경해주는 함수이다.
ToDoBoxList.js 코드
```javascript
const moveToDoBox = async (toDoBoxId, toIndex, shouldCallApi) => {
let box;
toDoBoxList.forEach((toDoBox)=>{ //filter사용시 계속 오류가 나서 일단 이렇게 작성하였다.
console.log(toDoBox);
if(toDoBox.id === toDoBoxId)
box = toDoBox;
})
const index = toDoBoxList.indexOf(box);
let newOrder = [...toDoBoxList];
newOrder.splice(index, 1);
newOrder.splice(toIndex, 0, box);
setToDoBoxList(newOrder)
if(shouldCallApi) {
// 서버에 순서 저장 api 요청
}
};
결과