[React] Drag기능 만들기

노유성·2023년 5월 2일
0
post-thumbnail

⭐Lists 컴포넌트 전체 소스코드

import React from 'react'
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';

export default function Lists({ todoList, setTodoList }) {

    const handleDelete = (id) => {
        let newTodoList = todoList.filter(item => {
            return item.id !== id;
        })
        setTodoList(newTodoList);
    }


    const handleComplete = (id) => {
        let newTodoList = todoList.filter(item => {
            if (item.id === id) {
                item.completed = !item.completed;
            }
            return item;
        })

        setTodoList(newTodoList);
    }

    const handleEnd = (result) => {
        console.log(result)

        // 변경된게 없으면 함수 종료
        if (!result.destination) return;

        // 불변성을 지켜주기 위해서 데이터를 카피해서 가져옴
        const newTodoList = todoList;

        // 재배열될 아이템을 잘라서 가져옴
        const [reorderdItem] = newTodoList.splice(result.source.index, 1);

        // 재배열된 아이템을 도착지 인덱스에 추가함 -> 위치 변경 완료
        newTodoList.splice(result.destination.index, 0, reorderdItem);
        setTodoList(newTodoList);

    }

    return (
        <div>
            <DragDropContext onDragEnd={handleEnd}>
                <Droppable droppableId='Unique-droppable-id' >
                    {(provided) => (
                        <div {...provided.droppableProps} ref={provided.innerRef}>
                            {todoList.map((data, index) => (
                                <Draggable
                                    key={data.id}
                                    draggableId={data.id.toString()}
                                    index={index}
                                >
                                    {(provided, snapshot) => (
                                        <div key={data.id}
                                            {...provided.draggableProps}
                                            {...provided.dragHandleProps}
                                            ref={provided.innerRef}
                                            className={`${snapshot.isDragging ? "bg-gray-400" : "bg-gray-100"} 'flex item-center justify-between w-full px-4 py-1 my-2 text-gray-600 bg-gray-100 border rounded`}>

                                            <div className='item-center'>
                                                <input type="checkbox"
                                                    defaultChecked={false}
                                                    onChange={() => handleComplete(data.id)} />
                                                <span 
                                                className={data.completed ? "line-through" : undefined}>{data.title}</span>
                                                <button
                                                    className='px-4 py-2 float-right'
                                                    onClick={() => handleDelete(data.id)}>X</button>
                                            </div>
                                        </div>
                                    )}
###                                 </Draggable>
                            ))}
                            {provided.placeholder}
                        </div>
                    )}
                </Droppable>
            </DragDropContext>
        </div>
    )
}

🪐컴포넌트 소개

DragDropContext, Droppable, Draggable 컴포넌트들을 계층적으로 넣어주어야 한다.

Draggable 컴포넌트는 드래그를 할 수 있는 컴포넌트이고 Droppable 컴포넌트는 해당 섹터 안에서 Draggable 컴포넌트를 내려놓을 수 있는 컴포넌트이다. 즉 Draggable 컴포넌트를 drop할 때, droppable 컴포넌트 내에다가 드랍을 해야만 컴포넌트를 이동시킬 수 있고 또 그 구역이 DragDropContext 컴포넌트 내에 있어야만 이벤트를 받아서 동작을 처리할 수 있다.

DragDropContext

  1. onDragEnd
    드래그가 종료되었을 때 발생하는 이벤트의 이벤트 리스너를 등록할 수 있다. 위 예제에서는 handleEnd() 함수를 등록하였다. 특정 이벤트가 발생하면은 브라우저에서 이벤트 리스너에게 이벤트를 전달해주는데 handleEnd()함수에도 동일하게 이벤트가 전달된다.
    드래그 이벤트에는 destination이라는 속성이 있는데 해당 속성을 통해서 현재 클릭된 컴포넌트의 도착 위치에 대한 정보를 알 수 있으며 해당 정보를 통해서 드래그 앤 드롭 이후에 변경된 순서를 반영하는 코드가 handleEnd()에 작성되어 있다.

Droppable

  1. droppableId
    우리가 하나의 페이지에 드래그 앤 드롭이 가능한 공간을 여러 개 정의할 수 있으므로 각각의 droppable component를 구분해주기 위해 id를 필수적으로 등록해야 한다.
  2. provided
    Droppable component를 사용하면 provided 객체를 얻을 수 있는데 해당 객체를 통해서 Droppable component 하위에 생성된 div 태그에 속성을 지정해주어야 한다. 왜 지정해주어야 하는 지는 나중에 알아보아야겠지만 일단 필수적이다.
    아래와 같이 전개 연산자를 통해서 속성들을 지정해주고 ref 속성에도 provided 객체의 innferRef 값을 할당해야 한다.
<Droppable droppableId='Unique-droppable-id' >
    {(provided) => (
         <div {...provided.droppableProps} ref={provided.innerRef}>
  1. provided.placeholder
    드래그를 시도하면 해당 컴포넌트는 Droppable component에서 사라지는 취급을 받게 되는데 그러면 해당 전체 화면의 크기가 줄어들게 되고 이는 사용자 입장에서 부자연스럽게 느껴진다. 이를 방지하기 위해서 드래그가 시도되더라도 화면의 크기를 hold하는 속성이다. (정확히는 div태그 안에 들어가는 값)

Draggable

  1. key, draableId, index
    Draggable component에도 Droppable component와 비슷하게 id와 key, index를 지정해야 하는데 각 값들은 고유해야한다. 하지만 Draggable은 이제 직접 드래그를 할 대상이기에 Draggable component 하위에 저장될 정보들을 이용해서 각 값들을 고유한 값으로 지정할 수 있다.
<Draggable
	key={data.id}
	draggableId={data.id.toString()}
	index={index}
> 
  1. provided, snapshot
    provided는 Droppable component에서랑 마찬가지로 하위 div태그에 속성을 주기 위해 Draggable에서 제공하는 객체이며 sanpshot은 드래그가 시도되었을 때 해당 드래깅의 정보가 저장된 객체이다.
    sanpshot에는 isDragging 이라는 property가 있는데 해당 property를 통해서 드래그가 되었는지에 대한 여부를 판단할 수 있다. 또한 그 여부를 통해서 드래깅이 되고 있는 컴포넌트에 특별한 시각적 효과를 더할 수 있다.
profile
풀스택개발자가되고싶습니다:)

0개의 댓글