[React] react-beautiful-dnd 으로 요소 드래그 되도록 만들기 3

2022. 4. 5. 00:07WEB Dev/Javascript | REACT | Node.js

728x90

 

 

 

이제 드래그는 되도록 만들었으니 드랍했을때 상태를 고정하는 함수를 만들어야 한다.

우리는 함수를 통해 정보를 전달해야하기는 하나 우선 화면을 바꾸는데 집중해서 함수를 만들어야 한다.

현재 적용하고 있는 함수는 onDragEnd 함수 하나밖에 없다.

 

onDragEnd의 인자로 어떤 것이 전달되는지 확인해보면 아래와 같다.

 

  const onDragEnd = (res) => {
    console.log("목표 드래그")
    console.log("res", res)
  }

 

 

 

 

우선 목표 드래그의 onDragEnd 함수에 res 라는 인자를 전달하고 console.log로 찍어볼 수 있도록 했다.

그랬더니 우리가 이전에 지정한 draggableId를 포함해 combine, destination, draggableId, mode, reason, source, type

들이 콘솔에 출력되었다.

 

 

 

 

 

 

여기서 우리가 드래그 하고 있는 것은 source 이다. source의 index를 보니 1번이고

destination은 내가 방금 1번 index의 목표를 끌어다 떨어뜨려 자리를 차지하게 만든 목표의 자리다. 해당 destination의 index는 3번이다.

 

간단하게 생각하면 index 1을 3으로 바꾸면 되는게 아닐까?

일단 간단하게 index를 구하는 함수를 만들어 보자 

 

우선 표시되는 목표들의 index를 변수로 받아본다.

 

 

  const onDragEnd = (res) => {
    //드래그 하는 sourced의 index
   const sourceOrderNo = res.source.index;
    //드래그 해서 내려놓은 destination의 index
    const destinationOrderNo = res.destination.index;
  }

 

 

이렇게 되면 받아오는 index 값들이 움직이고 있는 목표의 goalOrderNo과 내려놓는 자리의 goalOrderNo면 

state를 변경하기 쉬워질테니 이전에 만들어 둔 Draggable 의 draggableId의 값을 goalOrderNo로 바꿔준다.

여기서 key 값 또한 goalOrderNo 로 바꿔버리게 되면 

 

index.js:1 warning: encountered two children with the same key, `3`. keys should be unique so that components maintain their identity across updates. non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.

index.disc:1 경고: 같은 키 '3'을 가진 두 자녀가 발생했습니다.컴포넌트가 업데이트 후에도 ID를 유지하려면 키가 고유해야 합니다. non-displicate keys는 자식의 복제 및 누락의 원인이 될 수 있습니다.동작은 지원되지 않으며 향후 버전에서 변경될 수 있습니다.

 

이런 오류가 발생할 수 있다. 아예 이후 동작을 중단해버리니 key의 경우 배열의 순서를 뜻하는 index로 사용하자.

 

 

 return (
    <DragDropContext onDragEnd={onDragEnd}>
      <Droppable droppableId="Goal">
      {provided =>  (
        <div className="goals-list-wrap" {...provided.droppableProps} ref={provided.innerRef}>
          {todoDataArray.map((data, index) => {
            return (
              <Draggable draggableId={String(data.goalOrderNo)} index={data.goalOrderNo} key={index}>
               {provided => (
                  <div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
                    <OrderTodoGoal data={data} index={index}  id={data.goalId} />
                  </div>
                )}  
              </Draggable>
            )
          })}
          {provided.placeholder}
        </div>
      )}
      </Droppable>
    </DragDropContext>
  )

 

 

바꿔야 할 데이터의 구조는 아래와 같다.

 

 

// todo api
export const objTodosDataResult = atom({
  key: 'objTodosDataResult',
  default: [
  {
  goalTitle : "첫 번째 목표",
  goalId: 1,
  goalOrderNo : 1, //목표 순서
  goalTitleColor : "#ff0000",
  todos : [
       {goalId: 1,
        todoId: 1,
        orderNo: 1,
        title: "첫 번째 목표의 첫 번째 할 일",
        date: "2022-02-13",
        endRepeatDate: "2022-02-13",
        repeatDays: {
          THU: "N",
          WEN: "N",
          TUE: "N",
          SAT: "N",
          FRI: "N",
          MON: "N",
          SUN: "N"
        },
        checkYn: "N"
        },
        {goalId: 1,
          todoId: 2,
          orderNo: 2,
          title: "첫 번째 목표의 두 번째 할 일",
          date: "2022-02-13",
          endRepeatDate: "2022-02-14",
          repeatDays: {
            THU: "N",
            WEN: "N",
            TUE: "N",
            SAT: "N",
            FRI: "N",
            MON: "N",
            SUN: "N"
          },
          checkYn: "N"
      },
      {goalId: 1,
        todoId: 3,
        orderNo: 3, 
        title: "첫 번째 목표의 세 번째 할 일",
        date: "2022-02-13",
        endRepeatDate: "2022-02-17",
        repeatDays: {
          THU: "N",
          WEN: "Y",
          TUE: "N",
          SAT: "N",
          FRI: "Y",
          MON: "N",
          SUN: "N"
        },
        checkYn: "N"
      },
  ]},
  {
    goalTitle : "두 번째 목표",
    goalId: 2,
    goalOrderNo : 2,//목표 순서
    goalTitleColor : "#ff873d",
    todos : [
         {goalId: 2,
          todoId: 1,
          orderNo: 1,
          title: "두 번째 목표의 첫 번째 할 일",
          date: "2022-02-13",
          endRepeatDate: "2022-02-13",
          repeatDays: {
            THU: "N",
            WEN: "N",
            TUE: "N",
            SAT: "N",
            FRI: "N",
            MON: "N",
            SUN: "N"
          },
          checkYn: "N"
          },
          {goalId: 2,
            todoId: 2,
            orderNo: 2,
            title: "두 번째 목표의 두 번째 할 일",
            date: "2022-02-11",
            endRepeatDate: "2022-02-15",
            repeatDays: {
              THU: "N",
              WEN: "N",
              TUE: "N",
              SAT: "N",
              FRI: "N",
              MON: "N",
              SUN: "N"
            },
            checkYn: "N"
        },
    ]},
    {
      goalTitle : "세 번째 목표",
      goalId: 3,
      goalOrderNo : 3,//목표 순서
      goalTitleColor : "#0119cb",
      todos : [
           {goalId: 3,
            todoId: 1,
            orderNo: 1,
            title: "세 번째 목표의 첫 번째 할 일",
            date: "2022-02-16",
            endRepeatDate: "2022-02-16",
            repeatDays: {
              THU: "N",
              WEN: "N",
              TUE: "N",
              SAT: "N",
              FRI: "N",
              MON: "N",
              SUN: "N"
            },
            checkYn: "N"
            },
            {goalId: 3,
              todoId: 2,
              orderNo: 2,
              title: "세 번째 목표의 두 번째 할 일",
              date: "2022-02-13",
              endRepeatDate: "2022-02-19",
              repeatDays: {
                THU: "N",
                WEN: "N",
                TUE: "N",
                SAT: "N",
                FRI: "N",
                MON: "N",
                SUN: "N"
              },
              checkYn: "N"
          },
      ]},
      {
        goalTitle : "네 번째 목표",
        goalId: 4,
        goalOrderNo : 4,//목표 순서
        goalTitleColor : "#77ab59",
        todos : [
             {goalId: 4,
              todoId: 1,
              orderNo: 1,
              title: "네 번째 목표의 첫 번째 할 일",
              date: "2022-02-16",
              endRepeatDate: "2022-02-16",
              repeatDays: {
                THU: "N",
                WEN: "N",
                TUE: "N",
                SAT: "N",
                FRI: "N",
                MON: "N",
                SUN: "N"
              },
              checkYn: "N"
              },
        ]},
]
})

 

 

이 중첩 배열에서 드래그 하는 목표의 goalOrderNo를 찾아서 드랍되는 목표의 goalOrderNo로 바꿔주는 코드를 작성해보자.

 

 

위의 objTodosDataResult를 미리 todoDataArray로 딥카피를 해주고 있는데, 이 때 [...Array] 보다 JSON.parse(JSON.stringfy(Array)); 로 하는 것이 오브젝트 불변 오류가 나지 않는다.

 

 

todoDataArray를 map으로 검사하는데 어떤 목표의 goalOrderNo값이 현재 움직이고 있는 sourceOrderNo 값과 같다면

그 목표의 goalOrderNo값을 destinationOrderNo로 대체하는 함수를 작성했다.

 

이것을 실행해보면 다음과 같다.

 

  const onDragEnd = (res) => {
    console.log("res", res)
    //드래그 하는 sourced의 index
    const sourceOrderNo = res.source.index;
    //드래그 해서 내려놓은 destination의 index
    const destinationOrderNo = res.destination.index;
    
    //orderNo 교체하기
    todoDataArray.map((data)=>{
      if(data.goalOrderNo === sourceOrderNo){
        data.goalOrderNo = destinationOrderNo;
      }
    })
  }

 

실제 콘솔로 찍어보면 아래와 같다.

 

 

 

 

 

 

이제 이렇게 바뀐 todoDataArray를 setState를 통해서 변경되도록 하면 될 것 같다.

우선 원본을 바꾸는 setState 함수 이름을 setDtTodos로 정해두었다.

recoil을 쓰고 있어 아래와 같이 변수에 할당해두었다.

 

 

  let [dtTodos, setDtTodos] = useRecoilState(objTodosDataResult);

 

 

이 setDtTodos를 onDragEnd 맨 마지막에 실행될 수 있도록 해준다.

 

 

  const onDragEnd = (res) => {
    //드래그 하는 sourced의 index
    const sourceOrderNo = res.source.index;
    //드래그 해서 내려놓은 destination의 index
    const destinationOrderNo = res.destination.index;
    todoDataArray.map((data)=>{
      if(data.goalOrderNo === sourceOrderNo){
        data.goalOrderNo = destinationOrderNo;
      }
    })
    setDtTodos(todoDataArray)
}

 

 

이제 실제 데이터가 잘 변하는지 콘솔을 찍어보자

 

 

 

 

 

변경된다~~~

 

 

 

 

728x90