drag n drop 기능 구현하기

이경준·2021년 2월 12일
3

todoList

목록 보기
7/8
post-custom-banner

기존에 react.js로 진행중이던 todoList의 기능을 추가하던중 drag and drop기능을 추가하여 보았다.

1.react-beautiful-dnd 설치

//npm 설치
yarn add react-beautiful-dnd

rbd는 drag and drop을 쉽게 구현할수 있게 해주는 npm이다.

2. ul부모태그 감싸기

//기존 컴포넌트 (todoList.jsx)
  return (
    <Styled.ListContainer>
      {todoReducer.map((list, index) => (
        <TodoItem/>
      ))}
    </Styled.ListContainer>
  );
//DragDropContext, Droppable로 감싸기
import {
  DragDropContext,
  Droppable,
} from "react-beautiful-dnd";

  return (
    <DragDropContext> //*
      <Droppable> //*
        <Styled.ListContainer>
          {todoReducer.map((list, index) => (
            <TodoItem/>
          ))}
        </Styled.ListContainer>
      </Droppable>  
    </DragDropContext> 
  );

1.import {DragDropContext, Droppable} from "react-beautiful-dnd" 한다.
2. 드래그할 리스트의 가장 부모 태그를 래핑한다. (provider와 비슷한 기능으로 생각하면된다.)

3. droppable 영역 설정

  return (
    <DragDropContext>
      <Droppable droppableId='droppableLists'> // *
        {(provided) => { //*
          return (
            <Styled.ListContainer
              {...provided.droppableProps} //*
              ref={provided.innerRef} //*
            >
              {todoReducer.map((list, index) => (
                <TodoItem/>
              ))}
            </Styled.ListContainer>
          );
        }}
      </Droppable>
    </DragDropContext>
  );
  1. droppableId를 설정한다. (특정 인스턴스를 추적할수있다.)
  2. 가장상위 태그인 ul태그에 props와 ref를 준다.

4. draggable 영역 설정

  return (
    <DragDropContext onDragEnd={onDragEndHandeler}>
      <Droppable droppableId='droppableLists'>
        {(provided) => {
          return (
            <Styled.ListContainer
              {...provided.droppableProps}
              ref={provided.innerRef}
            >
              {todoReducer.map((list, index) => (
                <Draggable //*
                  key={list.id} //*
                  draggableId={`${list.id}`} //*
                  index={index} //*
                >
                  {(provided) => ( //*
                    <TodoItem
                      provided={provided} //*
                    />
                  )}
                </Draggable>
              ))}
            </Styled.ListContainer>
          );
        }}
      </Droppable>
    </DragDropContext>
  );
  1. Draggable로 drag될 리스트 태그를 감싼다.
  2. draggableId, index를 설정한다. (id와 index는 같으면 안된다.)
    3.provided 를 props로 주입한다.

5. Draggable props와 ref 설정

//todoItem.jsx
return (
    <Styled.ListContainer
      ref={provided.innerRef} //*
      {...provided.draggableProps} //*
      {...provided.dragHandleProps} //*
    >
      {todoList}
    </Styled.ListContainer>
  );

여기까지하면 기본설정이 끝난것이다. 그러나 드래그를 해보면 에러가 나고 리스트의 사단 모양이 잡히지 않는다.

6. placeholder 설정

  return (
    <DragDropContext onDragEnd={onDragEndHandeler}>
      <Droppable droppableId='droppableLists'>
        {(provided) => {
          return (
            <Styled.ListContainer
              {...provided.droppableProps}
              ref={provided.innerRef}
            >
              {todoReducer.map((list, index) => (
                <Draggable 
                  key={list.id} 
                  draggableId={`${list.id}`} 
                  index={index} 
                >
                  {(provided) => ( 
                    <TodoItem
                      provided={provided} 
                    />
                  )}
                </Draggable>
              ))}
              {provided.placeholder} //*
            </Styled.ListContainer>
          );
        }}
      </Droppable>
    </DragDropContext>
  );

이제 하단 모양이 드래그시 잘 잡혀있다.

7.기능 구현

const onDragEndHandeler = (result) => {
  console.log(result.source)
  console.log(result.destination)
};

<DragDropContext onDragEnd={onDragEndHandeler}>
  1. Context에 onDragEnd props로 result를 확인해보면 드래그할 태그와 최종 드롭되는 태그를 확인 가능하다.

const onDragEndHandeler = (result) => {
    const currentList = todoReducer;

    const startTagIndex = result.source.index;
    const destinationTagIndex = result.destination.index;

    const [startTag] = Array.from(currentList).splice(startTagIndex, 1);

    dispatch(listMove({ startTagIndex, startTag, destinationTagIndex }));
  };

2.todoList는 reduxToolkit을 사용하고 있었기에 todoReducer에서 list를 받아왔다.
2-1. currentList에서 splice로 드래그할 태그를 뺀다
2-2. dispatch를 이용하여 (startTagIndex, startTag, destinationTagIndex) 를 payload로 보내준다.

8. reducer에서의 기능구현

//todoReducer
export const todoReducer = createSlice({
  name: "todos",
  initialState: [],
  reducers: {
    listMove: (state, { payload }) => {
      state.splice(payload.startTagIndex, 1);
      state.splice(payload.destinationTagIndex, 0, payload.startTag);
    },
  },
});

state에서 드래그할 태그의index를 삭제 후 drop할 태그의 인덱스전으로 다시 집어넣는 기능을 구현하였다.

profile
내가 기억하기위한 블로그
post-custom-banner

0개의 댓글