[react] Drag&Drop 구현하기

Suyeon·2020년 12월 12일
6

React

목록 보기
14/26

드래그&드롭을 구현하기 위해서 react-DnD 라이브러리를 사용했다.

Installation

  • npm i react-dnd react-dnd-html5-backend
  • npm i immutability-helper

Setup

1️⃣ index.js에서DndProvider로 앱을 감싸준다.

// index.js
import React from 'react';
import ReactDom from 'react-dom';
import App from './App';
import { BrowserRouter as Router } from 'react-router-dom';

import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';

ReactDom.render(
    <DndProvider backend={HTML5Backend}> // (*)
      <Router>
        <App />
      </Router>
    </DndProvider>
  document.querySelector('#root')
);

2️⃣ dnd.js파일을 생성하고 사용할 itemType을 정의한다. 파일명과 타입(예시: CARD)이름은 본인 마음대로 사용하면 된다.

// dnd.js
export const ItemTypes = {
  CARD: 'card',
};

Implement

3️⃣ List.jsmoveCard 함수를 생성한다. 이 예제에서는 immutability-helper를 사용했다.

List.js

// List.js
import React, { useState, useCallback } from 'react';
import styled from 'styled-components';
import update from 'immutability-helper';

import Item from 'components/Item/Item';

const Wrapper = styled.div`
  border: 1px solid blue;
  width: 80%;
  margin: 0 auto;
  background-color: ${props => (props.isOver ? 'pink' : 'white')};
`;

const List = () => {
  const [cards, setCards] = useState([
    { id: 1, title: 'First' },
    { id: 2, title: 'Second' },
    { id: 3, title: 'Third' },
  ]);

  const moveCard = useCallback( // (**) Reorder an array
    (dragIndex, hoverIndex) => {
      const dragCard = cards[dragIndex];

      setCards(
        update(cards, {
          $splice: [
            [dragIndex, 1], // Delete
            [hoverIndex, 0, dragCard], // Add
          ],
        })
      );
    },
    [cards]
  );

  return (
    <Wrapper>
      {cards.map((item, index) => (
        <Item
          index={index}
          id={item.id}
          title={item.title}
          moveCard={moveCard}
          key={item.id}
        />
      ))}
    </Wrapper>
  );
};

export default List;

4️⃣ Item.js에서 드래그&드롭 기능을 구현한다.

import React, { useRef } from 'react';
import styled from 'styled-components';

import { ItemTypes } from '../utils/item';
import { useDrag, useDrop } from 'react-dnd';

const Wrapper = styled.div`
  border: 1px solid blue;
  opacity: ${props => (props.isDragging ? 0 : 1)};
  background-color: green;
`;

const Item = ({ id, title, index, moveCard }) => {
  const ref = useRef(null); // (*)

  const [, drop] = useDrop({ // (*)
    accept: ItemTypes.CARD,
    hover(item, monitor) {
      if (!ref.current) {
        return;
      }

      const dragIndex = item.index;
      const hoverIndex = index;

      if (dragIndex === hoverIndex) {
        return;
      }

      const hoverBoundingRect = ref.current?.getBoundingClientRect();
      const hoverMiddleY =
        (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
      const clientOffset = monitor.getClientOffset();
      const hoverClientY = clientOffset.y - hoverBoundingRect.top;

      if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
        return;
      }

      if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
        return;
      }

      moveCard(dragIndex, hoverIndex);
      item.index = hoverIndex;
    },
  });

  const [{ isDragging }, drag] = useDrag({ // (*)
    item: { type: ItemTypes.CARD, id, index },
    collect: monitor => ({
      isDragging: monitor.isDragging(),
    }),
  });

  drag(drop(ref)); // (*)

  return (
    <Wrapper ref={ref} isDragging={isDragging}>
      <p>{title}</p>
    </Wrapper>
  );
};

export default Item;

DnD(Drag and Drop) 활용한 모습


참고 자료
React DnD 공식 홈페이지
Playing around with react-dnd

profile
Hello World.

0개의 댓글