프론트엔드/React

함수형 컴포넌트에서 react-beautiful-dnd 적용하기

고줭 2022. 5. 5. 17:17

drag and drop의 기능이 필요해 라이브러리를 찾던 중 react-beautiful-dnd 라이브러리를 찾았는데 클래스형으로는 예제가 있지만 함수형은 없는 것같아 검색했습니다.

코드샌드박스에 누군가 잘 올려준 게시물 덕분에 해결

https://codesandbox.io/s/9z5tn?file=/src/index.js 

 

vertical-list - CodeSandbox

vertical-list using react, react-beautiful-dnd, react-dom

codesandbox.io

 

저같은 경우 App.js에서 <PageList /> 에게 props로 [pageList, setPageList]를 넘겨주고 이를 pageList 컴포넌트에서 받아 react-beautiful-dnd로 렌더하는 식입니다.

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

// a little function to help us with reordering the result
const reorder = (list, startIndex, endIndex) => {
  const result = Array.from(list);
  const [removed] = result.splice(startIndex, 1);
  result.splice(endIndex, 0, removed);

  return result;
};

const grid = 8;

const getItemStyle = (isDragging, draggableStyle) => ({
  // some basic styles to make the items look a bit nicer
  userSelect: "none",
  padding: grid * 2,
  margin: `0 0 ${grid}px 0`,

  // change background colour if dragging
  background: isDragging ? "lightgreen" : "grey",

  // styles we need to apply on draggables
  ...draggableStyle,
});

const getListStyle = (isDraggingOver) => ({
  background: isDraggingOver ? "lightblue" : "lightgrey",
  padding: grid,
  width: 500,
  position: "relative",
});

const queryAttr = "data-rbd-drag-handle-draggable-id";

const InputList = (props) => {
  const [placeholderProps, setPlaceholderProps] = useState({});
  const { pageList, setPageList } = props;
  // const [items, setItems] = useState(getItems(10));

  const onDragEnd = (result) => {
    // dropped outside the list
    if (!result.destination) {
      return;
    }

    setPlaceholderProps({});
    setPageList((items) =>
      reorder(items, result.source.index, result.destination.index)
    );
  };

  const onDragUpdate = (update) => {
    if (!update.destination) {
      return;
    }
    const draggableId = update.draggableId;
    const destinationIndex = update.destination.index;

    const domQuery = `[${queryAttr}='${draggableId}']`;
    const draggedDOM = document.querySelector(domQuery);
    if (!draggedDOM) {
      return;
    }
    const { clientHeight, clientWidth } = draggedDOM;

    const clientY =
      parseFloat(window.getComputedStyle(draggedDOM.parentNode).paddingTop) +
      [...draggedDOM.parentNode.children]
        .slice(0, destinationIndex)
        .reduce((total, curr) => {
          const style = curr.currentStyle || window.getComputedStyle(curr);
          const marginBottom = parseFloat(style.marginBottom);
          return total + curr.clientHeight + marginBottom;
        }, 0);

    setPlaceholderProps({
      clientHeight,
      clientWidth,
      clientY,
      clientX: parseFloat(
        window.getComputedStyle(draggedDOM.parentNode).paddingLeft
      ),
    });
  };

  // Normally you would want to split things out into separate components.
  // But in this example everything is just done in one place for simplicity
  return (
    <DragDropContext onDragEnd={onDragEnd} onDragUpdate={onDragUpdate}>
      <Droppable droppableId="droppable">
        {(provided, snapshot) => (
          <div
            {...provided.droppableProps}
            ref={provided.innerRef}
            style={getListStyle(snapshot.isDraggingOver)}
          >
            {pageList.map((item, index) => (
              <Draggable
                key={`item${item.index}`}
                draggableId={`item-${item.index}`}
                index={index}
              >
                {(provided, snapshot) => (
                  <div
                    ref={provided.innerRef}
                    {...provided.draggableProps}
                    {...provided.dragHandleProps}
                    style={getItemStyle(
                      snapshot.isDragging,
                      provided.draggableProps.style
                    )}
                  >
                    <label>index: {index}</label>
                    <br />
                    <label>item.index: {item.index}</label>
                    <br />
                    <label>component: {item.component}</label>
                    <div>
                      <img src={item.url} alt="" height="200" />
                      <br />
                      <label>url: {item.url}</label>
                    </div>
                  </div>
                )}
              </Draggable>
            ))}

            {provided.placeholder}
            {/* <CustomPlaceholder snapshot={snapshot} /> */}
            <div
              style={{
                position: "absolute",
                top: placeholderProps.clientY,
                left: placeholderProps.clientX,
                height: placeholderProps.clientHeight,
                background: "tomato",
                width: placeholderProps.clientWidth,
              }}
            />
          </div>
        )}
      </Droppable>
    </DragDropContext>
  );
};

export default InputList;

바꾼건 <Droppable /> 안에 items.map() -> pageList.map() 이랑 <Draggable />안에 key, draggableId, 내용물 정도입니다.

draggableId에다가 숫자만 넣으면 requires string~~~ 이라는 경고문이 나오는데 이는 앞에 문자열을 붙여주면 해결.

사진이 제대로 변함!!!!