듀얼 셀렉터

하성화·2022년 3월 5일
0

팀 프로젝트로 듀얼 셀렉터를 구현하게 되었다.

내가 맡은 부분은 아이템을 왼쪽, 오른쪽으로 옮기는 기능이었다.
contorl+Click을 하면 아이템을 추가로 선택을 할 수 있게 해야하고, shift+Click을 누르면 시작부터 끝까지 선택되게 구현해야 했다.

클릭 이벤트

선택한 아이템의 클릭 로직 부터 구현하였다.

 // 선택된게 하나도 없으면 추가
if (selectId.length === 0) {
  setSelectId([id]);
  return;
}
// 이미 있는 아이디 일 시
if (selectId.findIndex((item) => item === id) !== -1) {
  let data = [];
  if (selectId.length !== 1) {
    data = (prev) => prev.filter((element) => element !== id);
  }
  setSelectId(data);
} else {
  setSelectId([id]);
}

일단 선택이 되면 선택된 아이테의 id값을 추가 하였고, 이미 존재하는 아이디이면 삭제를 하였다.

데이터 이동

그 후, 선택된 아이디 값을 이용하여 해당하는 데이터를 옮기는 코드를 구현하였다.

// Left -> Right
if (direction) {
  let copyData = Object.assign([], leftData);
  selectId.map((id) =>
               leftData.map((data) => {
    if (id === data.id) {
      copyData.splice(
        copyData.findIndex((item) => item.id === id),
        1
      );
      moveItem = [...moveItem, { ...data }];
    }
  })
              );
  setLeftData(copyData);
  setRightData((prev) => [...prev, ...moveItem]);
}

왼쪽의 데이터에서 선택된 아이디를 삭제하고 오른쪽 데이터에 옮겨 주었다.

다중 이동

마지막으로 여러개의 아이템을 선택하는 로직을 구현하였다.
데이터 이동구현 시 여러개의 아이템이 들어왔을 때 도 고려하여 작성하였기 때문에 클릭 이벤트만 수정하여 구현을 마무리 하였다.

SHIFT

if (selectedNum && e.shiftKey) {
  let copyData = Object.assign([], list);
  let select = [];
  const start = firstClick < idx ? firstClick : idx;
  const end = firstClick < idx ? idx : firstClick;

  copyData.slice(start, end + 1).map((item) => select.push(item.id));

  setSelectId(select);
  return;
}

e.shiftKey를 이용하면 shiftkey의 입력을 확인할 수 있다.
첫 클릭 인덱스를 기억해 두고 쉬프트 클릭이 들어왔을 때, 두 인덱스를 비교하여 음수값이 나오지 않도록 범위를 지정하였고, 지정된 범위의 id를 selectId로 등록 하였다.

CTRL

if (selectedNum && e.ctrlKey) {
  setFirstClick(idx);
  setSelectId((prev) => [...prev, id]);
  return;
}

컨트롤도 쉬프트와 마찬가지로 e.ctrlKey를 이용하여 구현하였다. 컨트롤 클릭 시 계속 selectId를 추가하도록 구현하였다.

Error

어렵지 않은 기능 구현이었지만, 생각지도 못한 에러를 경험하였다.
아래의 코드는 아이템의 이동을 구현했던 오류가 발생했던 코드이다.

if (direction) {
  let moveItem = [];
  setLeftData((prev) => {
    let copyData = Object.assign([], prev);

    selectId.map((id) =>
                 leftData.map((data, index) => {
      if (id === data.id) {
        leftData.splice(index, 1);
        moveItem = [...moveItem, data];
      }
    })
                );
    return copyData;
  });
  // moveItem --> null
  setRightData((prev) => [...prev, ...moveItem]);
    }

로컬 환경에서는 잘 동작했던 코드가 배포가 된 후 제대로 동작하지 않았다.
오류의 이유는 setState의 동작이 비동기이기 때문에 발생한 오류였다.
평소 데이터 업데이트 시 콜백을 이용하여 값을 업데이트 하였다.
위 코드에서 선택된 id를 담아서 오른쪽으로 넘겨 주어야 하는데
setState의 동작이 비동기이기 때문에 setLeftData에서 moveItem이 만들어지기도 전에 setRightData가 동작하여 moveItem은 비어있는 값이 되서 발생한 오류였다.

동작화면

0개의 댓글