들어가기에 앞서 react-beautiful-dnd가 어디에서 어떻게 나온 라이브러리인가하면, Trell와 Jira를 만든 회사에서 배포하는 오픈 소스 라이브러리라고 한다. 트렐로의 경우 부드럽고 자연스러운 애니메이션 모션으로 To-Do 어플리케이션중에서도 부드러운 사용성을 보여주고 있다.
이제, 앞서 챕터5까지 만들었던 To Do List application을 React-beautiful-dnd 라이브러리를 통해 To Do List 프로젝트를 Kanvan board로 업그레이드하고 드래그 앤 드롭 모션을 적용해보자.
dnd 라이브러리의 DragDropContext, Droppable, Draggable에 대해 알아보자.
<DragDropContext />
: Drag and Drop 애니메이션을 사용하고자 하는 어플리케이션의 영역을 감싸는 wrapper. <Droppable />
과 <Draggable />
컴포넌트를 props로 사용해야한다. React-redux Provider와 비슷한 구조로 쓰이며, DragDropContent를 nested하게 쓸 수 없다. 또한, Responder로 onDragEnd 이벤트를 필수로 한다. Responder는 Hook과 같이 쓰이며 발생한 드래그 이벤트와 State update나 Style update, screen announcement등을 동기화하는데에 사용된다.
기본 사용법
import React from 'react';
import { DragDropContext } from 'react-beautiful-dnd';
function App() {
// using useCallback is optional
const onBeforeCapture = useCallback(() => {
/*...*/
}, []);
const onBeforeDragStart = useCallback(() => {
/*...*/
}, []);
const onDragStart = useCallback(() => {
/*...*/
}, []);
const onDragUpdate = useCallback(() => {
/*...*/
}, []);
const onDragEnd = useCallback(() => {
// the only one that is required
}, []);
return (
<DragDropContext
onBeforeCapture={onBeforeCapture}
onBeforeDragStart={onBeforeDragStart}
onDragStart={onDragStart}
onDragUpdate={onDragUpdate}
onDragEnd={onDragEnd}
>
<div>Hello world</div>
</DragDropContext>
);
}
<Droppable />
: DragDropContext 내에서 Drop을 할 수 있는 영역이자, Draggable 영역을 감싸는 wrapper. 반드시<Draggable />
컴포넌트나 ReactElement를 반환하는 Childeren Node를 갖고 있어야한다. 또한 Droppable는 props로 Identifier인 Droppable Id를 필수로 요구한다. Droppable이 올바른 DOM node를 인식하게 하기 위해, provided.InnerRef
를 통해 최상위 DOM node에 접근할 수 있게 한다.
기본 사용법
<Droppable droppableId="droppable-1">
{(provided, snapshot) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
Good to go
{provided.placeholder}
</div>
)}
</Droppable>
<Draggable />
: DragDropContext 내에서 Drag가 가능한 컴포넌트를 감싸는 wrapper. 반드시 <Droppable />
컴포넌트 내부에 있어야 한다. Draggable 컴포넌트 또한, draggableId
props를 필요로 하며, index
props를 통해 Droppable 컴포넌트 내부에서 Draggable
컴포넌트의 순서 정렬을 맞추어 준다. 보통은 리스트 map에 의해 작성한다. Children Component로 ReactNode를 반환하는 함수를 가진다. Node는 draggableProps와 dragHandleProps를 참조하는데, dragHandleProps를 하위 child의 props로 만들어주면 Drag 영역을 한정할 수 있다. 아래 코드에서, one과 two의 코드를 비교하여 이해해보자. one의 경우, Drag HERE!!을 드래그해야 움직일 수 있고, two의 경우는 어떠한 영역에서도 드래그가 가능하다.
기본 사용법
// App.tsx
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
function App() {
const onDragEnd = () => {};
return (
<DragDropContext onDragEnd={onDragEnd}>
<div>
<Droppable droppableId="one">
{(provided) => (
<ul ref={provided.innerRef} {...provided.droppableProps}>
<Draggable draggableId="first" index={0}>
{(provided) => (
<li
ref={provided.innerRef}
{...provided.draggableProps}
>
<span {...provided.dragHandleProps}>Drag HERE</span>
one
</li>
)}
</Draggable>
<Draggable draggableId="second" index={1}>
{(provided) => (
<li
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
two
</li>
)}
</Draggable>
</ul>
)}
</Droppable>
</div>
</DragDropContext>
);
}
export default App;
주의사항
wrapper의 position에 fixed나, margin collapse가 일어나지 않도록 주의한다.