드래그 앤 드롭 라이브러리
Source Code
https://github.com/kangilbin/React.js/tree/master/DragAndDrop
1. 라이브러리 설치
npm i react-beautiful-dnd
/* typscript */
npm i --save-dev @types/react-beautiful-dnd
( index .tsx )
<React.StrictMode> // 삭제
...
</React.StrictMode>
- React 18 버전에서의 StrictMode를 지원하지 않기 때문에 index파일에서 StrictMode를 지워야 한다.
- DragDropContext : 드래그 앤 드롭이 가능하게 하고자 하는 영역 감싼다.
- Droppable : 어떠한 것을 드래그 앤 드롭할 수 있는 영역
- Draggable : Droppable 영역 안에서 실제 드래그하는 영역
2. 드래그 앤 드롭 구현 예시
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
function App() {
const onDragEnd = () => {};
return (
<DragDropContext onDragEnd={onDragEnd}>
<div>
<Droppable droppableId="one">
{(provided, snapshot) => (
<ul ref={provided.innerRef} {...provided.droppableProps}>
<Draggable draggableId="first" index={0}>
{(provided, snapshot) => (
<li
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
One
</li>
)}
</Draggable>
<Draggable draggableId="second" index={1}>
{(provided) => (
<li
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
Two
</li>
)}
</Draggable>
</ul>
)}
</Droppable>
{provided.placeholder}
</div>
</DragDropContext>
);
}
export default App;
- placeholder : 드롭되더라도 리스트의 사이즈를 그대로 유지시켜준다.
- ref : beautiful-dnd가 HTML요소에 접근할 수 있도록 설정해 준다.
- 무조건 Draggable의 key와 draggableId가 같아야 한다.
2-1) Droppable, Draggable : 자식 요소는 react 요소이면 안된다 함수여야 한다.
<Droppable droppableId="one">
{(provided, snapshot) => (
<Draggable draggableId="first" index={0}>
{(provided, snapshot) => (
// 함수 방식
)}
)}
</Droppable>
- 첫 번째 param : provided를 가진다.
- 두 번째 param : snapshot를 가진다 card(Draggable), board(Droppable)의 색상을 변경할 때 사용
2-2) Droppable, Draggable의 provided
( Droppable Props )
<ul ref={provided.innerRef} {...provided.droppableProps}> </ul>
( Draggable Props )
<li
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
One
</li>
- 모든 prop을 주면 드래그, 드롭, 애니메이션이 적용된다.
2-3) 특정 부위를 드래그 가능하게 하는 방법
<li ref={provided.innerRef} {...provided.draggableProps}>
<span {...provided.dragHandleProps}>👌</span>
Two
</li>
- dragHandleProps : 해당 prop을 가지고 있는 요소에서만 드래그가 가능하다.
3. onDragEnd
const onDragEnd = (args: any) => {
console.log(args);
};
- 드래그 앤 드롭 이후 발생하는 이벤트
- destination : 이동하고자 하는 droppable과 인덱스 정보가 담겨있다.
- source : 선택한 droppable과 인덱스가 정보가 담겨있다.
4. react memo (부분 렌더링) 활용하기
드래그 앤 드롭 시 전부 렌더링 된다. react memo는 prop이 바뀌지 않는다면 컴포넌트를 렌더링 하지 않는다.
https://ko.reactjs.org/docs/react-api.html#reactmemo
import React from "react";
function DraggableCard({ toDo, index }: IDraggableCardProps) {
...
}
export default React.memo(DraggableCard);
- React.memo(DraggableCard) : prop이 변하지 않았다면 DraggableCard를 다시 렌더링 하지 않는다.
5. Snapshot을 이용한 색상 변경
5-1) Board 색상 변경
...
interface IAreaProps {
isDraggingOver: boolean;
isDraggingFromThis: boolean;
}
const Area = styled.div<IAreaProps>`
background-color: ${(props) =>
props.isDraggingOver ? "pink" : props.isDraggingFromThis ? "red" : "blue"};
flex-grow: 1;
transition: background-color .3s ease-in-out;
`;
function Board({ toDos, boardId }: IBoardProps) {
return (
<Wrapper>
<Title>{boardId}</Title>
<Droppable droppableId={boardId}>
{(provided, snapshot) => (
<Area
isDraggingOver={snapshot.isDraggingOver}
isDraggingFromThis={Boolean(snapshot.draggingFromThisWith)}
ref={provided.innerRef}
{...provided.droppableProps}
>
{toDos.map((toDo, index) => (
// key 와 draggableId는 무조건 같아야 한다.
<DraggableCard key={toDo} toDo={toDo} index={index} />
))}
{provided.placeholder}
</Area>
)}
</Droppable>
</Wrapper>
);
}
export default Board;
- isDraggingOver : board 위로 드래그해서 들어오고 있는지 알려준다.
- draggingFromThisWith : 해당 board로부터 드래그를 시작했는지도 알려준다. 즉, 유저가 어떤 board를 떠난다면 그 board로 부터 drag를 시작했다는 뜻
- draggingFromThis : 현재 board를 떠난다면 (결과 값은 draggableId 또는 undefind)
5-2) Card 색상 변경
...
const Card = styled.div<{ isDragging: boolean }>`
padding: 10px 10px;
border-radius: 5px;
margin-bottom: 5px;
background-color: ${(props) =>
props.isDragging ? "tomato" : props.theme.cardBgColor};
`;
interface IDraggableCardProps {
toDo: string;
index: number;
}
function DraggableCard({ toDo, index }: IDraggableCardProps) {
console.log(toDo, "has been rendered");
return (
<Draggable draggableId={toDo} index={index}>
{(provided, snapshot) => (
<Card
isDragging={snapshot.isDragging}
ref={provided.innerRef}
{...provided.dragHandleProps}
{...provided.draggableProps}
>
{toDo}
</Card>
)}
</Draggable>
);
}
export default React.memo(DraggableCard);
반응형
'React' 카테고리의 다른 글
[React] Framer Motion(2) 드래그(Drag) 추적 값(useMotionValue) 사용 방법 (1) | 2022.09.30 |
---|---|
[React] Framer Motion(1) 애니메이션 효과 (1) | 2022.09.29 |
[ReactTS] Recoil State(상태) 관리 (0) | 2022.09.14 |
[React] React Hook Form 사용한 Validation(검증) 처리 방법 (0) | 2022.09.06 |
[React] ApexCharts.JS 차트 사용 방법 (0) | 2022.08.30 |