본문 바로가기

카테고리 없음

react typescript 7 정리

Recoil selector set에 대해서

set 함수는 원하는 state가 어떤 것이든지 그걸로 수정하도록 해주는 기능
selector도 atom과 동일하게 array형태로 [value, setValue] 형태로 준다.

// setter 사용법
 const onHoursChange = (event: React.FormEvent<HTMLInputElement>) => {
    setHours(+event.currentTarget.value);
  };

// 구현부
set: ({set}, newValue) => {
    const minutes = Number(newValue) * 60;
    set(minuteState, minutes);
}

drag and drop (react-beautiful-dnd)

react로 드래그 앤 드랍을 지원하는 라이브러리

npm i react-beautiful-dnd
npm i --save-dev @types/react-beautiful-dnd
  • DragDropContext
    • 드래그 앤 드랍을 포함할 부분
    • children을 항상 필요로 한다.
  • onDragEnd
    • 유저가 드래그를 끝낸 시점에 불려지는 함수
  • Droppable
    • 드롭할 수 있는 영역
    • id를 필요로한다.
    • children을 필요로하는데 children은 함수여야한다.
  • Draggable
    • 드래그할 영역
    • id를 필요로한다.
    • children을 필요로하는데 children은 함수여야한다.
      <DragDropContext onDragEnd={onDragEnd}>
        <div>
          <Droppable droppableId="one">
            {() => (
              <ul>
                <Draggable draggableId="first" index={0}>
                  {() => <li>One</li>}
                </Draggable>
                <Draggable draggableId="second" index={1}>
                  {() => <li>Two</li>}
                </Draggable>
              </ul>
            )}
          </Droppable>
        </div>
      </DragDropContext>

droppable에서 주는 인자

  • provided
    • innerRef
    • draggableProps : 요소가 기본적으로 드래그 되기를 원할 때
    • dragHandleProps : 모든 곳 말고 특정한 부분에서만 드래그하게 하고 싶을 때

아래 코드의 경우 불을 통해서만 드래그 이벤트가 일어남

<Droppable droppableId="one">
            {(magic) => (
              <ul ref={magic.innerRef} {...magic.droppableProps}>
                <Draggable draggableId="first" index={0}>
                  {(magics) => (
                    <li ref={magics.innerRef} {...magics.draggableProps}>
                      <span {...magics.dragHandleProps}>🔥</span>
                      One
                    </li>
                  )}
                </Draggable>
              </ul>
            )}
</Droppable>
  • provided.placeholder는 드래그할 동안 영역을 고정시켜서 사이즈가 변하는 것을 막음
    • draggable을 움직일 때 리스트가 작아지는 현상 방지
<Board ref={magic.innerRef} {...magic.droppableProps}>
   {toDos.map((toDo, index) => (
      <Draggable key={`todo-${index}`} draggableId={toDo} index={index}>
        {(magics) => (
            <Card ref={magics.innerRef} {...magics.dragHandleProps} {...magics.draggableProps}>
               {toDo}
            </Card>
        )}
       </Draggable>
     ))}
    {magic.placeholder}
</Board>

하지만 아직 리스트를 움직인다고 해서 위치가 바뀌는 효과는 없음.
onDragEnd와 recoil을 사용하면 정렬 기능을 넣을 수 있다.

  • onDragEnd : 드래그가 끝났을 때 실행되는 함수
    • destination : 드래그의 목적지
    • source : 드래그의 시작지
      이 두 가지의 정보를 사용하여 위치를 조정할 수 있다.

소스의 인덱스의 위치를 지우고 목적지의 인덱스 위치를 추가하여 shift해주는 전략

  • source의 index 지우기

    // source의 index 지우기
    toDosCopy.splice(source.index, 1)
  • destination의 index 추가

    // destination의 index 추가
    toDosCopy.splice(destination?.index, 0, draggableId)

하지만 아직 문제가 있는데 연산 시간의 문제로 인해, 순간 텍스트가 이상하게 보인다.
랜더링하는 과정에서 문제가 있는데 f를 처음으로 옮겨도 a, b, c, d, e, f 모두 다시 랜더링되기 때문
아래의 방법으로 해결할 수 있다.

React Memo

변동에 영향이 없는 컴포넌트까지 다시 랜더링 되는 것을 막아준다.

export default React.memo(DraggableCard)

보드를 여러 개 만들기

done, doing, to-do의 보드를 만들어본다.

  • object -> array
    • Object.prototype.keys을 사용하면 object의 keys를 배열로 만들어준다.
const toDoState = atom<IToDoState>({
  key: 'toDo',
  default: {
    to_do: ['a', 'b'],
    doing: ['c', 'd', 'e'],
    done: ['f'],
  },
})

const [todos, setTodos] = useRecoilState(toDoState)
const toDos = Object.keys(todos)

보드 내 위치 바꾸기

onDragEnd 내에서 처리를 해주는데 보드 내이므로
목적지의 id와 소스의 id가 같을 때만 실행하도록 해야함

나머지는 비슷한데, ...을 사용하여 레퍼런스 복사가 아니라 전 상태와 동일한 새 배열을 만들어서 적용해줘야한다.
등록돼있는 모든 보드를 가져와서, 보드 내 상태를 바꾼 후 변한 상태를 기존에 적용시키는 방식

 setToDos((allBoards) => {
        const boardCopy = [...allBoards[source.droppableId]];
        boardCopy.splice(source.index, 1);
        boardCopy.splice(destination?.index, 0, draggableId);
        return {
          ...allBoards,
          [source.droppableId]: boardCopy,
        };
      });

보드 간 위치 바꾸기

  1. source의 보드에서 해당 원소를 제거
  2. destination의 보드에서 해당 원소를 추가
  3. 위 두 가지의 보드를 전체 보드에 반영
setToDos((allBoards) => {
        const sourceBoard = [...allBoards[source.droppableId]]
        const destinationBoard = [...allBoards[destination.droppableId]]

        sourceBoard.splice(source.index, 1)
        destinationBoard.splice(destination?.index, 0, draggableId)

        return {
          ...allBoards,
          [source.droppableId]: sourceBoard,
          [destination.droppableId]: destinationBoard,
        }
      })

추가적인 기능

내가 보드를 떠나는지 도착하는지에 따라서 area의 색상을 변경해주고 싶음
이 때 필요한 것이 droppable의 두 번째 인자인 snapshot

snapshot
+ isDraggingOver : 유저가 보드 위로 드래그해서 들어오고 있는지 알려줌
+ draggingFromThisWith : 유저가 해당 보드로부터 드래그를 시작했는지 알려줌

일단 시간 상 여기까지만 하고 나머지 시간에 채워넣을게요...ㅜ

코드 레포지토리