npm i react-beautiful-dnd
버전이 안 맞으면 npm i react-beautiful-dnd --legacy-peer-deps
로 의존성을 무시하고 설치한다.
React 18
이상에서는 작동하지 않는다.
ts
환경은 npm i --save-dev @types/react-beautiful-dnd
를 설치해 타입을 알려준다.
React 18
을 지원하는 @hello-pangea/dnd
로 대체했다.
ts
도 지원하며 문법은 react-beautiful-dnd
와 같다.dnd(drag and drop)
을 가능하게 하고 싶은 부분을 설정한다.onDragEnd
함수와 자식 요소를 필요로 한다.onDragEnd
는 사용자의 드래그가 끝나는 시점을 알리는 역할이다.import { DragDropContext } from "@hello-pangea/dnd";
function App() {
const onDragEnd = () => {};
return (
<DragDropContext onDragEnd={onDragEnd}>
<div></div>
</DragDropContext>
);
}
export default App;
drop
하는 영역이다.droppableId
를 요구한다.React Elements
가 아닌 함수로 구성되어야 한다.import { Droppable } from "@hello-pangea/dnd";
function App() {
...
return (
...
<Droppable droppableId="one">{() => <ul></ul>}</Droppable>
...
);
}
export default App;
drag
하는 영역이다.draggableId
와 index
, 자식 요소를 필요로 한다.import { Draggable } from "@hello-pangea/dnd";
function App() {
...
return (
...
<ul>
<Draggable draggableId="first" index={0}>
{() => <li>One</li>}
</Draggable>
<Draggable draggableId="second" index={1}>
{() => <li>Two</li>}
</Draggable>
</ul>
...
);
}
export default App;
innerRef
와 droppableProps
를 요소에 제공해야 한다.innerRef
와 draggableProps
, dragHandleProps
를 요소에 제공해야 한다.dragHandleProps
는 드래그 가능한 특정 영역을 지정한다.Draggable
컴포넌트가 끝나는 지점 아래에 droppableProvided.placeholder
를 추가한다.import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
const itemList = ["a", "b", "c", "d", "e", "f"];
function App() {
const onDragEnd = () => {};
return (
<DragDropContext onDragEnd={onDragEnd}>
<div>
<Droppable droppableId="one">
{(droppableProvided) => (
<ul
ref={droppableProvided.innerRef}
{...droppableProvided.droppableProps}
>
{itemList.map((item, index) => (
<Draggable key={item} draggableId={item} index={index}>
{(draggableProvided) => (
<li
ref={draggableProvided.innerRef}
{...draggableProvided.draggableProps}
{...draggableProvided.dragHandleProps}
>
{item}
</li>
)}
</Draggable>
))}
{droppableProvided.placeholder}
</ul>
)}
</Droppable>
</div>
</DragDropContext>
);
}
export default App;
itemList
의 요소를 그 자리에 고정하려면 onDragEnd
함수를 이용해 배열를 수정해야 한다.args
는 DropResult
형태를 가지고 있으며, 움직인 요소는 draggableId
와 source
로, 드랍한 자리는 destination
으로 알 수 있다.itemList
는 다음 로직으로 재구성할 수 있다.const itemList = ["a", "b", "c", "d", "e", "f"];
// "a"를 "index:3"으로 이동한다고 했을 때
itemList.splice(0, 1); // "a"의 index, 제거할 요소 개수
itemList.splice(3, 0, "a"); // 삽입할 index, 제거할 요소 개수, 삽입할 요소
console.log(itemList); // ['b', 'c', 'd', 'a', 'e', 'f']
참고 MDN - splice
const onDragEnd = ({ draggableId, destination, source }: DropResult) => {
if (!destination) return;
itemList.splice(source.index, 1);
itemList.splice(destination?.index, 0, draggableId);
};
Draggable
의 자식 요소가 움직이면 그것을 감싼 부모 요소의 상태가 모두 변경되기 때문에 React
는 그 횟수만큼 re-rendering
한다.React.memo
는 prop
이 바뀌지 않는 한 컴포넌트를 렌더링하지 않게끔 한다.Draggable
컴포넌트를 분리한 후 React.Memo
로 감싼다.// DraggableComponent.tsx
import React from "react";
function DraggableComponent({ item, index }) {
return (
<Draggable draggableId={item} index={index}>
{(draggableProvided) => (
<li
ref={draggableProvided.innerRef}
{...draggableProvided.draggableProps}
{...draggableProvided.dragHandleProps}
>
{item}
</li>
)}
</Draggable>
);
}
export default React.memo(DraggableComponent);
참고