TRELLO CLONE(4) - Drag and drop에 form 추가

초연2e·2022년 10월 8일
0

MUTSA_Front.archive

목록 보기
10/16

이제 list에 todo를 추가할 수 있는 form을 만들 것이다.
즉, 이제 유저가 직접 todo를 쓰고 엔터를 쳐서 등록을 할 수 있게 만들어주자.



Refs


일단 reference에 대해 알아보자.
우리는 사실 이미 reference를 사용했다. (뭔지는 모르고)

reference : 리액트를 이용해 HTML 요소를 지정하고 가져올 수 있는 방법. 우리가 HTML 요소를 가져와서 그걸 변형할 수 있게 해준다.

input에 연결된 버튼을 클릭했을 때 input에 focus되도록 만들어주고싶다.
추가로 5초 후에 input에서 포커스를 사라지게 해주고싶다.

	...
    
    function Board({ toDos, boardId }: IBoardProps) {
      const inputRef = useRef<HTMLInputElement>(null);
      const onClick = () => {
        inputRef.current?.focus();
        setTimeout(() => {
          inputRef.current?.blur();
        }, 5000);
      };
      return (
        <Wrapper>
        <Title>{boardId}</Title>
        <input ref={inputRef} placeholder="grab me" />
        <button onClick={onClick}>click me</button>
      
      ...

이렇게 해주면 된다.
inputRef를 사용함으로써 우리는 input에 대한 접근을 할 수 있게 된다. (useRef import 필요)

쉽게 말하면 자바스크립트에서 document.getElement~~ 했던 것과 같은 거라고!!

우리가 원하던 대로 잘 작동한다.




toDo 작성 form 만들기


form을 만들기 위해 react-hook-form에서 useForms를 import해서 사용할 것임.

이제 form의 interface를 만들어주고 (toDo:string;) form에 그 interface를 넣어주자.

interface IForm {
    toDo: string;
}

function Board({ toDos, boardId }: IBoardProps) {
    const { register, setValue, handleSubmit } = useForm<IForm>();

이런식으로 ~.~
그 후 styled-components로 Form을 만들어주자.

그런 다음

      <Form>
        <input
          {...register("toDo", { required: true })}
          type="text"
          placeholder={`Add task on ${boardId}`}
        />
      </Form>

이렇게~ 해줌

이제 form이 valid할 때 호출되는 함수를 만들어주자.

    const onValid = ({ toDo }: IForm) => {
      setValue("toDo", "");
    };

이걸 우리 form의 onSubmit에게 줄건데 react-hook-form을 통해 handleSubmit을 먼저 호출해야한다.

  <Form onSubmit={handleSubmit(onValid)}>

이렇게 ㅇㅇ 지금까지 계속 전에 했던 것과 똑같다.

모아보면

interface IForm {
    toDo: string;
}

function Board({ toDos, boardId }: IBoardProps) {
    const { register, setValue, handleSubmit } = useForm<IForm>();
    const onValid = ({ toDo }: IForm) => {
      setValue("toDo", "");
    };
  return (
    <Wrapper>
      <Title>{boardId}</Title>
      <Form onSubmit={handleSubmit(onValid)}>
        <input
          {...register("toDo", { required: true })}
          type="text"
          placeholder={`Add task on ${boardId}`}
        />
      </Form>

	...

이런식!

이러면 우리가 valid한 form을 submit할 때 마다 우리 input은 빈칸이 된다!




이제 우리는 data로부터 오는 toDo를 실제로 만들어야한다.
여기에서 오류가 굉장히 많이 생긴다,,

일단 atoms.tsx에 가서

import {atom} from "recoil";

export interface ITodo {
  id: number;
  text: string;
}

interface IToDoState {
  [key: string]: ITodo[];
}

export const toDoState = atom<IToDoState>({
  key: "toDo", 
  default: {
    "To Do": [],
    Doing: [],
    Done: [],
    },
});

코드를 이렇게 수정해주자.

그 후 Board.tsx에 가서

interface IBoardProps {
    toDos: ITodo[];
    boardId: string;
}

이렇게 ITodo[]로 바꾸어주어야한다.
왜냐하면 우리 board는 toDos가 그냥 string array인 줄 아는데 이젠 ITodo의 array가 되었기 때문이다.

근데 이러면 또 아래에서 다른 오류가 생긴다.
toDos가 object라 map에서 key로 사용할 수 없기 때문이다. 대신 toDo.id가 되겠지..

뭐 등등 발생하는 오류들을 해결해나가면 된다,,,
오류가 ㄱㅖ~속계속생겨남


어쨌든 오류를 해결해나가다보면 다른 오류는 다 해결되고App에서 draggableId만 오류가 나는 때가 생긴다.

이전에 우리 board들은 string으로만 이루어진 array라 draggableId를 사용할 수 있었다.
그런데 이제는 destinationBoard안에 draggableId를 줄 수 없다.

우리는 object를 삭제하기 전에 먼저 object를 grab해야한다!

우리는 이제 어디에있는 card가 움직이는지를 받아올 수 있으니까 그 id정보를 이용해서 toDo의 내용을 받아야한다.

어딘가에 먼저 저장해두고, 삭제한 뒤, 다시 더해줘야한다.


이제는 draggableId가 toDo의 id만 의미하니까 다른 방법으로 toDo의 value를 찾아야한다.




board의 복사본을 가져다가 taskObj를 만들고 boardCopy와 같다고 하자.

const taskObj = boardCopy[source.index]; 해주면 이게 우리가 옮기고자 하는 todo object 전체를 가져다 줄 것임~
그리고 splice에서 draggableId가 들어갔던 곳을 taskObj로 모두 바꿔주자. string만을 받는 것이 아니라 이제는 진짜로 toDo Object를 받는 것이 되는거지!!

아래에서도 똑같이 해준다.
(const taskObj = sourceBoard[source.index];)



그리고 이제 Board로 가서!

    const setToDos = useSetRecoilState(toDoState);
    const { register, setValue, handleSubmit } = useForm<IForm>();
    const onValid = ({ toDo }: IForm) => {
        const newToDo = {
            id: Date.now(),
            text: toDo,
            };
            setToDos((allBoards) => {
                return {
                    ...allBoards,
                    [boardId]: [newToDo, ...allBoards[boardId]],
                };
        });
      setValue("toDo", "");
    };

이렇게 해주자.
보다시피 newToDo를 선언해줬다.
id는 Date.now()로 설정해주었고 text는 우리가 data에서 가져올 toDo와 같은 값~

그럼 이제 이 newToDo를 board안에 넣어줘야한다.
일단 atom을 import하고 atom을 위한 set함수를 만들어준다.
이를 위해 useSetRecoilState를 사용해 toDoState를 import해왔다!

이제 setToDos를 만들고 이전의 board들을 다 가져다가 새 array로 return해주었다. boardId도 받아왔다.

원래 있던 board들과 현재 우리가 있는 board들을 return하고 기존의 board 내용에 우리 newToDo를 더해주는 방식이다.
( [boardId]: [newToDo, ...allBoards[boardId] 의 순서로 작성했기 때문에 우리가 새롭게 toDo를 작성하면 리스트의 맨 위로 등록된당📌)




이제 우리가 각 board의 form에 toDo를 입력하고 엔터를 누르면 그 투두가 해당 보드에 등록되고, Drag and Drop해서 왔다리갔다리 하게 만들 수도 있다. 👍👍👍👍👍

profile
프론트로 멋쟁이되기 대장정,,

0개의 댓글