TRELLO CLONE(2) - Drag and Drop

초연2e·2022년 10월 7일
0

MUTSA_Front.archive

목록 보기
8/16

Drag and Drop을 만들어보자!

React-beautiful-dnd

React로 list를 만들기 위한 아름답고 접근 가능한 드래그 앤 드롭

설치방법
: 터미널에 npm i react-beautiful-dnd 입력

우리는 typescript에서 사용할 것이므로 추가로 터미널에 npm i --save-dev @types/react-beautiful-dnd 해줘야함.

react beautiful dnd는 DragDropContext와 Droppable, Dragggable이 있다.




3가지 영역


DragDropContext

먼저 DragDropContext는 드래그앤드롭을 가능하게 하고 싶은 앱의 한 부분임~!

DragDropContextonDragEnd를 필요로 한다.
onDragEnd함수는 유저가 드래그를 끝낸 시점에 불려지는 함수임.

Droppable

우리가 어떤 것을 드롭할 수 있는 영역을 말함.

Droppable은 droppableId를 필요로 한다.
유저가 드롭할 수 있는 영역이 두가지 이상일 수 있기 때문!
추가로 droppable의 children은 함수 형태여야 함,,!

Draggable

우리가 어떤 것을 드래그할 수 있는 영역을 말한다.

Droppable과 마찬가지로 draggableId를 필요로 하고 children이 함수형태여야한다~! 추가로 index도 필요로 한다.

즉,

각각의 리스트는 무언가를 드롭할 수 있는 영역으로 droppable한다. 리스트에 있는 item들은 draggable하겠지?!

(아, 이 세가지는 모두 import해줘야 사용가능)




provided

droppable과 draggable의 첫번째 argument로 이름은 그냥 마음대로 지어줘도 된다.
강의에서는 magic으로 지어주었으므로 걍 나는 magic을 사용했다.

draggable의 경우 만약 요소가 기본적으로 드래그되길 원한다면 draggableProps를 하면 되는데, dragHandleProps를 쓰면 원하는 곳 어디서든지 카드를 집어 드래그할 수 있다.

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <div>
        <Droppable droppableId='one'>
          {(magic) => (
          <ul ref={magic.innerRef} {...magic.droppableProps}>
            <Draggable draggableId='first' index={0}>
              {(magic) => 
                <li
                  ref={magic.innerRef}
                  {...magic.draggableProps} 
                  {...magic.dragHandleProps}
                  >
                    One
                </li>
              }
            </Draggable>
            <Draggable draggableId='second' index={1}>
              {(magic) => 
                <li
                  ref={magic.innerRef}
                  {...magic.draggableProps} 
                  {...magic.dragHandleProps}
                >
                  Two
                </li>
              }
            </Draggable>
          </ul>
        )}
        </Droppable>
      </div>
    </DragDropContext>
  );

만약 유저로 하여금 li를 어떤 위치에서든지 드래그해서 옮기게 하고 싶다면 li에 draggableProps와 dragHandleProps를 넣어주면 된다고 함.

dragHandleProps를 이용하면 드래그 적용되게 하는 범위를 설정할 수 있음~,~

(ref 빼먹지말자!)

이렇게 해주고 검사창에서 elements를 확인해보면
위와 같이 prop들이 매우 많이 생긴 것을 알 수 있다.
이것들은 모두 우리가 어떤 것을 드래그하기 위해 필요한 prop들이라고 함!


이제 우리 App 실행 화면을 보면!
화면에 뭔가 마우스가 손바닥 모양이 되었을 것이다.
One과 Two가 이제 드래그가 될 것이다.. 움직인다.

근데 이대로만 해주면 드롭해줬을 때 바로 원상복귀된다.
이건 우리가 드롭이 끝났을 때 할 일을 정해주지 않았기 때문이다.




Draggable array로 만들기

const toDos = ["a", "b", "c", "d", "e", "f"];

	...
    
    {toDos.map((toDo, index) => <Draggable draggableId={toDo} index={index}>
      {(magic) => 
        <Card
          ref={magic.innerRef}
          {...magic.draggableProps} 
          {...magic.dragHandleProps}
   		>
      	{toDo}
       </Card>
    }
      </Draggable>
    )}
    
    ...

이런식으로 써주면 draggable을 array로 만들 수 있다.
map 이용하기 ㅋ.ㅋ

+) 추가로 코드에 {magic.placeholder} 추가하면 draggable이 빠져나가도 리스트의 사이즈는 그대로 유지된다! 유용한 도우미.




아이템 드롭시 재정렬

위에서는 그냥 toDos로 선언해주었던 리스트를 toDoState라는 atom으로 선언해주자.

export const toDoState = atom({
    key: "toDo",
    default: ["a", "b", "c", "d", "e", "f"],
  });

그리고 useRecoilState를 사용하자!

내가 하고싶은 것은 드래그가 끝났을 때 리스트의 인덱스를 변화시키는 것이다. (리스트 재정렬)
이를 위해서는 예전의 index값과 드래그 직후 인덱스 값을 알아야한다.
onDragEnd에 주목하자..

  const [toDos, setToDos] = useRecoilState(toDoState);
  const onDragEnd = ({ destination, source }: DropResult) => {};

onDragEnd에서 source(최근 드래그된 항목의 원래 index)와 destination(바꾸고싶은 인덱스)을 받아온다.


알고리즘은 이렇게된다.
먼저 source를 그 위치에서 삭제한다. 그 후 destination 위치의 index에 우리가 방금 삭제한 걸 넣는다!




splice


const x = [ "a","b","c","d","e","f"];
의 코드가 있고 우리가 예를 들어 a, 즉 index 0을 지우고 싶다고 해보자.

이 경우, x.splice(0,1) 하면!
splice는 index 0으로 가서 1개의 아이템을 삭제한다.

splice는 이런식으로 작동한다.👍
이게 첫번째 단계이다.

그럼 이제 우리가 a를 index2에 넣고 싶다고 해보자.
그럼 다시 x.splice를 해서 a를 2번 자리에 넣고 싶다고 말해야한다.

이 경우 x.splice(2,0,"a")를 해주면 된다.
이 코드는 index 2에서 시작해서 어떤 것도 지우지 말고 a를 집어넣으라는 말과 같당



그럼 우리는 splice를 이용해서 우리가 원하는 결과를 만들어낼 수 있겠다!!!!!!

한 번 해보자.




splice를 이용해서 재정렬 완료하기


onDragEnd 함수를 수정한다.
먼저 현재의 값을 가져와야겠지? 이름은 oldToDos로 해주자.
그리고 우리는 새롭게 수정된 array를 return해주어야한다.
모든 toDos를 변형시킬 수 없기 때문에
const toDosCopy = [...oldToDos]; << 이렇게 복사본을 만들어주자.

이제 source.index에서 item을 삭제하자.
toDosCopy.splice(source.index, 1) 해주면 우리의 item은 삭제된다.

그럼 이제 이 item을 다시 destination.index에 넣자.
toDosCopy.splice(destination?.index, 0, draggableId);

추가로 destination이 아니면 걍 return하라는 조건도 달아준다.


이제 재정렬이 잘 될 것이다~!
그러나 지금은 다시 렌더링하는 과정에서 약간의 간극이 생긴다. 뒤틀려보임,,,


다음엔 이걸 해결해보자.

profile
프론트로 멋쟁이되기 대장정,,

0개의 댓글