앞서 만든 todo 리스트를 기반으로 이번에는 칸반보드 형식으로 만들어 볼 것이다.
드래그 앤 드롭 : react-beautiful-dnd 라이브러리 사용
드래그 앤 드롭을 구현하기 위해 라이브러리를 설치한다.
npm i react-beautiful-dnd
DragDropContext
드래그 앤 드롭을 가능하게 하고 싶은 앱의 한 부분이다.
onDragEnd를 필수 프로퍼티로 갖는다.
Droppable / Draggable
각각 드롭/ 드래그 할 수 있는 영역이다.
고유성을 가지기 위해 id를 갖는다.
자식 컴포넌트는 무조건 함수타입이여야 한다.
기본 구조는 다음과 같다.
<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} {...provided.dragHandleProps}>First</li>}
</Draggable>
<Draggable draggableId="second" index={1}>
{(provided) => <li ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>Second</li>}
</Draggable>
</ul>
)}
</Droppable>
</div>
</DragDropContext>
싱글보드 만들기
스타일 컴포넌트를 사용하여 css를 꾸미고 todo atom을 만들어서 연결해주어 아래와 같은 간단한 보드를 만들었다 .
드래그를 하는 동안 보드의 크기가 축소되기 때문에 이를 방지하기 위해 draggable의 속성을 사용할 수 있다.
{provided.placeholder}
draggable 요소를 드래그 하는 동안 영역이 고정되어 있게 한다.
주로 draggable노드의 형제로 렌더링 한다.
드래그를 마치면 다시 원래대로 돌아간다 이를 방지하기 위해 onDragEnd 함수를 정의해줘야 한다.
onDragEnd 함수
드래그가 끝날 때 실행되는 함수이다.
드래그 정보를 인자로 받는다.
draggableId: 드래그 되었던 Draggable의 id
type: 드래그 되었던 Draggable의 type
source: Draggable이 시작된 위치
destination: Draggable이 끝난 위치
여기서 draggableId와 destination, source를 받아온다.
타입은 DropResult로 지정한다.
const onDragEnd = ({ draggableId, destination, source }: DropResult) => {
if (!destination) return;
setToDos((oldToDos) => {
const toDosCopy = [...oldToDos];
toDosCopy.splice(source.index, 1);
toDosCopy.splice(destination?.index, 0, draggableId);
return toDosCopy;
});
};
draggable의 키는 무조건 draggableId와 같아야 에러가 발생하지 않는다.
<Draggable key={toDo} draggableId={toDo} index={index}>
todo state 변경을 할 때 리스트가 흔들리는 것처럼 보이는 현상이 있을 것이다.
이건 state 변경 시 일어나는 간극 때문에 발생한 일이다.
하나의 todo를 변경하여도 일단 state를 건들게 되면 모든 todo를 재렌더링 하게 되기 때문이다.
부모 컴포넌트의 state를 바꾸면 자식 컴포넌트의 모든 컴포넌트는 재렌더링 된다
react.memo는 prop이 바뀌지 않는 이상 재렌더링 되지 않는다.
export default React.memo(DragabbleCard);
멀티보드 만들기
우선 state 수정을 해준다.
기존 state는 배열을 하나만 가진 형태였지만 키와 값이 있는 객체를 담아준다.
export interface IToDoState {
[key: string]: string[];
}
export const toDoState = atom<IToDoState>({
key: "toDo",
default: {
to_do: ["a", "b"],
doing: ["c", "d", "e"],
done: ["f"],
},
});
보드 컴포넌트를 따로 분리해서 만들고 객체의 키를 받아와 map 함수로 보드를 생성한다.
<Boards>
{Object.keys(toDos).map((board) => (
<Board boardId={board} key={board} toDos={toDos[board]} />
))}
</Boards>
멀티보드에 드래그 앤 드롭 기능을 구현해보자.
드래그가 끝났을 때 실행되는 함수인 onDragEnd( )를 수정한다.
다음 코드는 동일 보드 내에서의 기능을 구현한 것이고 다른 보드 간 이동을 구현할 때는 시작보드와 타겟보드를 나누어 처리하면 된다.
const onDragEnd = ({ draggableId, destination, source }: DropResult) => {
if (destination?.droppableId == source.droppableId) {
setToDos((allBoards) => {
const copyBoard = [...allBoards[source.droppableId]];
copyBoard.splice(source.index, 1);
copyBoard.splice(destination?.index, 0, draggableId);
return {
...allBoards,
[source.droppableId]: copyBoard,
};
});
}
};
리턴할 때 디스트럭처링하여 객체 안에서 중복된 키 값에는 마지막에 선언한 프로퍼티를 사용하게 한다.
드래그 앤 드롭 스타일 적용하기
드래그 할 때 보드의 색상이 변하게 하는 것과 같은 스타일을 입힐 수 있다.
Droppable과 draggable이 제공하는 건 provided 말고 snapshot도 있다.
isDraggingOver: boolean
현재 선택한 Draggable이 특정 Droppable위에 드래깅 되고 있는지 여부 확인
draggingOverWith: ?DraggableId
Droppable 위로 드래그하는 Draggable ID
draggingFromThisWith: ?DraggableId
현재 Droppable에서 벗어난 드래깅되고 있는 Draggable ID
isUsingPlaceholder: boolean
placeholder가 사용되고 있는지 여부
Todo 직접 생성하기
ref 프로퍼티는 react 코드를 이용해 html요소를 지정하고 가져올 수 있는 방법이다.쉽게 말해 html에 접근하는 방법을 말한다. useRef 훅을 사용하여 ref를 만든다.
const inputRef = useRef<HTMLInputElement>(null);
const onClick = () => {
inputRef.current?.focus();
}
전에 배운 reack-hook-form 라이브러리를 사용하여 todo입력 폼을 만든다.
앞서 했던 보드에서는 todo가 단순 string이라는 가정 하에 만들었는데
이제 todo는 id와 text값을 가진 객체이다.
(대공사가 필요하단 뜻..?)
draggableId는 이제 객체의 id를 말하기 때문에 무조건 수정이 필요하다.
id를 사용해서 객체의 text값을 가져와야 한다.
'Tech > TypeScript' 카테고리의 다른 글
[Typescript / TS] 영화 웹 서비스 만들기 (0) | 2023.02.07 |
---|---|
[Typescript / TS] Frame motion을 사용한 애니메이션 (0) | 2023.01.29 |
[Typescript / TS] ToDo리스트 만들기 (0) | 2023.01.16 |
[Typescript / TS] Recoil을 사용한 state 관리 (0) | 2023.01.16 |
[Typescript / TS] 코인트래킹 어플 만들기 (0) | 2023.01.07 |