이제 list에 todo를 추가할 수 있는 form을 만들 것이다.
즉, 이제 유저가 직접 todo를 쓰고 엔터를 쳐서 등록을 할 수 있게 만들어주자.
일단 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~~
했던 것과 같은 거라고!!
우리가 원하던 대로 잘 작동한다.
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의 내용을 받아야한다.
어딘가에 먼저 저장해두고, 삭제한 뒤, 다시 더해줘야한다.
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해서 왔다리갔다리 하게 만들 수도 있다. 👍👍👍👍👍