치춘짱베리굿나이스
React-Beautiful-Dnd 본문
React-beautiful-dnd
설치
$> npm i react-beautiful-dnd
$> yarn add react-beautiful-dnd
npm 링크
https://www.npmjs.com/package/react-beautiful-dnd
yarn 링크
용례
드래그 앤 드롭 리스트를 손쉽게 구현가능한 라이브러리이다
react-dnd
는 사용자가 크기변경, 디자인, 애니메이션 등 세세한 부분의 커스터마이징이 가능한 대신 조금 복잡하고, 이번 과제에서는 일반적인 드래그 앤 드롭 순서변경만 구현하면 되기 때문에 커스터마이징 요소는 적은 대신 설정이 간편한 react-beautiful-dnd
를 택하게 되었다
드래그 앤 드롭을 onDragEnd
이벤트로 사용할 수 있게끔 해주기 때문에, 여타 기본 이벤트 (onClick
, onSubmit
등) 처럼 이벤트 핸들러를 추가해서 드래그 앤 드롭 이벤트를 제어할 수 있다
다만 단점은... 업데이트가 1년 전에 멈춰서 리액트 18버전을 사용하는 시점에 조만간 지원이 되지 않을 수도 있다는 것이다
fork 떠서 마저 업데이트를 진행하는 사람들이 있나 검색해봤는데 찾을 수 없었다
예시
<div className={styles.favoriteContainer}>
<ul>
{favoriteData.Movies.map((v: IMovie) => (
<li key={`fav-list-${v.imdbID}`}>
<MovieBlock movieData={v} />
</li>
))}
</ul>
</div>
ul
+ li
태그로 만든 간단한 리스트를 드래그 앤 드롭 리스트로 만들어줄 것이다
1. 드래그 앤 드롭 이벤트를 발생시킬 요소를 컨텍스트로 감싸주기
<div className={styles.favoriteContainer}>
<DragDropContext onDragEnd={handleDragEnd}>
<ul>
{favoriteData.Movies.map((v: IMovie) => (
<li key={`fav-list-${v.imdbID}`}>
<MovieBlock movieData={v} />
</li>
))}
</ul>
</DragDropContext>
</div>
드래그 앤 드롭을 적용하고자 하는 리스트를 DragDropContext
로 감싸 준다
div
는 리스트의 배경을 담당하는 태그이기 때문에 가장 최상위로 빼내고, ul
리스트부터 감싸주었다
DragDropContext
는 onDragEnd
이벤트 핸들러 함수를 props
로 받는다
이 핸들러는 나중에 추가하도록 하자
2. 아이템 드롭이 이루어지는 컴포넌트를 Droppable로 감싸주기
<div className={styles.favoriteContainer}>
<DragDropContext onDragEnd={handleDragEnd}>
<Droppable droppableId='favoriteData'> {(droppableProv) => (
<ul className={styles.favoriteUL} {...droppableProv.droppableProps} ref={droppableProv.innerRef}>
{favoriteData.Movies.map((v: IMovie) => (
...
))}
{droppableProv.placeholder}
</ul>)}
</Droppable>
</DragDropContext>
</div>
내부의 li
가 드래그되는 요소이고, ul
이 리스트의 바깥에서 드롭을 감지하므로, ul
을 Droppable
컴포넌트로 감싸준다
droppableID
는 고유값으로 지정해주고, ul
을 화살표 함수로 한번 더 감싸준다
화살표 함수의 인자 droppableProv
에는 드롭을 구현하기 위한 props들 (droppableProv.droppableProps
)이 들어있으므로, 전개연산자를 통해 ul
태그에 props를 전달해주자
dnd 라이브러리가 ul
태그를 찾아 레퍼런스로 삼을 수 있도록 (내부 useRef
) ref에 droppableProv.innerRef
를 전달한다
ul
태그 내부의 끝에는 droppableProv.placeholder
를 추가하여 드롭 컴포넌트의 끝임을 명시한다
ul
컴포넌트가 점점 복잡해지고 있는 것은 기분탓이다
3. 아이템 드래그가 이루어지는 컴포넌트를 Draggable로 감싸주기
...
{favoriteData.Movies.map((v: IMovie, i: number) => (
<Draggable key={`fav-list-${v.imdbID}`} draggableId={`fav-list-${v.imdbID}`} index={i}>
{(DraggableProv) => (
<li
ref={DraggableProv.innerRef}
{...DraggableProv.dragHandleProps}
{...DraggableProv.draggableProps}
>
<MovieBlock movieData={v} />
</li>
)}
</Draggable>
))}
...
컴포넌트가 너무 복잡해져 map
함수로 리스트 요소들 (li
) 을 렌더링하는 부분만 잘라왔다
우리는 li
요소의 드래그로 이벤트가 발생되기를 원하기 때문에, li
요소를 Draggable
컴포넌트로 감싸준다
Droppable
과 비슷하게, draggableID
는 고유 id
값을 넣어주면 되고, 인덱스를 지정해주어야 한다
이 id값 덕분에 드롭 박스에서 어떤 요소가 드래그되었는지 인식하고, 인덱스를 지정하면 onDragEnd
이벤트 핸들러에서 어떤 인덱스의 요소가 어떤 인덱스로 이동했는지 정보를 인자로 보내주기 때문이다
Droppable
처럼 li
요소를 화살표 함수로 한번 감싸주고, li
요소에 드래그를 구현하기 위한 props를 전개연산자로 전달해 주었다 (DraggableProv.dragHandleProps
와 DraggableProv.draggableProps
)
draggableProps
는 드래그 시 애니메이션 등 스타일 적용을 위한 props들이 들어있고, dragHandleProps
는 드래그 이벤트 발생 여부, 이벤트가 발생한 인덱스 등의 제어를 위한 props들이 들어있다 (궁금해서 한번 까봤다)
4. onDragEnd 핸들러 구성하기
const handleDragEnd = (r: DropResult) => {
//
}
DragEnd
핸들러는 DropResult
타입의 인자를 하나 받는다
이 DropResult
인자 안에는 어떤 드래그 요소에서 이벤트가 발생했고, 해당 드래그 요소의 인덱스, 드롭했을 때 도착 지점의 인덱스 등을 담고 있다
핸들러 함수를 작성하려면 인자로 뭐가 들어오는지 알아야 하니 한번 출력해보자
source
는 드래그한 요소의 정보를 담고 있으며, 위의 예시에서는 ID가 favoriteData
인 드롭 박스 안에서 인덱스 1번의 요소를 드래그했음을 알 수 있다
destination
은 드래그한 요소가 드롭되었을 때 도착지점의 정보를 담고 있으며, 예시에서는 fav-list-tt0366548
이라는 ID의 요소가 0번 인덱스로 드롭되었음을 알 수 있다
드래그 앤 드롭 이벤트의 최종 목표는 거의 대부분 드래그한 요소의 순서를 바꾸는 것이므로, 핸들러 함수 내에서 이 r.source.index
와 r.destination.index
를 이용하여 배열을 조작하면 된다
최종 결과물
핸들러 함수에서 드롭한 요소의 순서를 바꾸어주었으며, 덕분에 순서변경이 일어나 드롭한 위치에 요소가 고정되는 것을 볼 수 있다
참고자료
https://github.com/atlassian/react-beautiful-dnd
https://velog.io/@yjs3819/react-beautiful-dnd
'ClientSide > 라이브러리' 카테고리의 다른 글
react-query 1. useQuery (0) | 2022.05.20 |
---|---|
gh-pages로 리액트 프로젝트 배포 + 404 에러처리 (1) | 2022.05.15 |
lodash (0) | 2022.05.14 |
store (0) | 2022.05.12 |
classnames (0) | 2022.05.06 |