[REACT] Refactoring
학습일 : 2023. 04. 19
지금부터는 할 일을 완료할 수 있는 기능을 추가해보고자 한다.
toDo의 상태를 바꿀 수 있어야 하니까 말이다.
클릭을 하면 TODO를 DOING이나 DONE으로 바꿀 수 있어야할 것이다.
(반대로도 가능해야 겠고.)
그 기능을 구현하고 나면 selectors라는 개념에 대해 공부해 볼 것이다.
selectors를 이용하면 state로부터 데이터를 만들 수 있다.
우선 수정할 부분이 있다.
코드를 이제 좀 간결하게 만들어 보기 위해
form 을 따로 빼서 components 폴더 밑에 CreateToDo.tsx 파일로 따로 분리시켜주고
li를 따로 빼서 ToDo.tsx로 분리,
atom도 atoms.tsx로 분리 하겠음.
<CreateToDo.tsx>
toDos는 필요없다.
수정말 할 수 있으면 되기 때문에 useSetRecoilState로 setToDos를 정의
toDoState를 atom으로 부터 불러오게됨.
import { useForm } from "react-hook-form";
import { useSetRecoilState } from "recoil";
import { toDoState } from "./atoms";
interface IForm {
toDo: string;
}
function CreateToDo() {
const setToDos = useSetRecoilState(toDoState);
const { register, handleSubmit, setValue } = useForm<IForm>();
const handleValid = ({ toDo }: IForm) => {
setToDos((oldToDos) => [
{ text: toDo, category: "TODO", id: Date.now() },
...oldToDos,
]);
setValue("toDo", "");
};
return (
<form onSubmit={handleSubmit(handleValid)}>
<input
{...register("toDo", {
required: "please write a to do",
})}
placeholder="오늘 해야할 일을 입력하세요"
/>
<button>add</button>
</form>
);
}
export default CreateToDo;
src 폴더에
atoms.tsx 파일 생성한 후
<atoms.tsx>
ToDoList.tsx에 있던
IToDo 인터페이스와 toDoState atom을 짤라서 붙여넣어준다.
그리고 앞에 export를 붙여넣어줌.
import { atom } from "recoil";
export interface IToDo {
text: string;
id: number;
category: "TODO" | "DOING" | "DONE";
}
export const toDoState = atom<IToDo[]>({
key: "toDo",
default: [],
});
이렇게 하면 CreateToDo에서 toDoState를 사용할 수 있게 된다.
<CreateToDo.tsx>
수정말 할 수 있으면 되기 때문에 useSetRecoilState로 setToDos를 정의
이젠 toDoState를 앞에서 따로 빼준 ataom으로 부터 불러오게됨.
IForm은 이곳에서만 쓰기 때문에 똑같이 ToDoList.tsx 에 있는 걸 복붙 해서 넣어주고
이 타입을 useForm에 적용시켜준다. useForm<IForm>();
import { useForm } from "react-hook-form";
import { useSetRecoilState } from "recoil";
import { toDoState } from "./atoms";
interface IForm {
toDo: string;
}
function CreateToDo() {
const setToDos = useSetRecoilState(toDoState);
const { register, handleSubmit, setValue } = useForm<IForm>();
const handleValid = ({ toDo }: IForm) => {
setToDos((oldToDos) => [
{ text: toDo, category: "TODO", id: Date.now() },
...oldToDos,
]);
setValue("toDo", "");
};
return (
<form onSubmit={handleSubmit(handleValid)}>
<input
{...register("toDo", {
required: "please write a to do",
})}
placeholder="오늘 해야할 일을 입력하세요"
/>
<button>add</button>
</form>
);
}
export default CreateToDo;
두번째로는, ul 태그 밑에 map으로 li 를 만들어 준 것도 별개의 컴포넌트로 분리하겠다.
역시나 components 폴더 밑 ToDo.tsx 파일로 분리시켜 주도록 한다.
<ToDo.tsx>
이제 여기서 key는 필요하지 않기 때문에 삭제해준다.
text prop은 반드시 필요하기 때문에
ToDo({text}) 로 전달해주고
좋은 소식은 이미 toDo 타입을 만들었다는 것임 atoms.tsx에 있음
따라서 TODO({text}:IToDo) 라고 적어주고 import 하면 끝난다.
import { IToDo } from "./atoms";
function ToDo({text}:IToDo) {
return <li>{text}</li>;
}
export default ToDo;
<ToDoList.tsx>
{...toDo} 이렇게 써줌으로 인해
prop으로 text, category, 등등
필수적으로 써주어야 하는 prop들을 써주어야 하는 노고가 줄어듦
import { useRecoilValue } from "recoil";
import CreateToDo from "./CreateToDo";
import { toDoState } from "./atoms";
import ToDo from "./ToDo";
function ToDoList() {
// 여기서는 useRecoilValue 값만 반환해주고 modifier 함수는 반환할 필요 없음
const toDos = useRecoilValue(toDoState);
return (
<div>
<h1>Thorn To Do</h1>
<hr />
{/* form 태그가 있던 자리 - CreateToDo.tsx로 따로 빼줬음*/}
<CreateToDo />
<ul>
{toDos.map((toDo) => (
// li가 있던 자리 - ToDo.tsx로 감
<ToDo {...toDo}/>
))}
</ul>
</div>
);
}
export default ToDoList;
코드를 정리해준 파일은 다음과 같다.
<ToDoList.tsx>
import { useRecoilValue } from "recoil";
import CreateToDo from "./CreateToDo";
import { toDoState } from "./atoms";
import ToDo from "./ToDo";
function ToDoList() {
// 여기서는 useRecoilValue 값만 반환해주고 modifier 함수는 반환할 필요 없음
const toDos = useRecoilValue(toDoState);
return (
<div>
<h1>Thorn To Do</h1>
<hr />
{/* form 태그가 있던 자리 - CreateToDo.tsx로 따로 빼줬음*/}
<CreateToDo />
<ul>
{toDos.map((toDo) => (
// li가 있던 자리 - ToDo.tsx로 감
<ToDo {...toDo}/>
))}
</ul>
</div>
);
}
export default ToDoList;
<CreateToDo.tsx>
import { useForm } from "react-hook-form";
import { useSetRecoilState } from "recoil";
import { toDoState } from "./atoms";
interface IForm {
toDo: string;
}
function CreateToDo() {
// toDos는 필요없다.
// 수정말 할 수 있으면 되기 때문에 useSetRecoilState로 setToDos를 정의
// toDoState를 ataom으로 부터 불러오게됨.
const setToDos = useSetRecoilState(toDoState);
const { register, handleSubmit, setValue } = useForm<IForm>();
const handleValid = ({ toDo }: IForm) => {
setToDos((oldToDos) => [
{ text: toDo, category: "TODO", id: Date.now() },
...oldToDos,
]);
setValue("toDo", "");
};
return (
<form onSubmit={handleSubmit(handleValid)}>
<input
{...register("toDo", {
required: "please write a to do",
})}
placeholder="오늘 해야할 일을 입력하세요"
/>
<button>add</button>
</form>
);
}
export default CreateToDo;
<atoms.tsx>
import { atom } from "recoil";
export interface IToDo {
text: string;
id: number;
category: "TODO" | "DOING" | "DONE";
}
// CreateToDo에서 이 toDoState를 사용할 수 있게 됨.
export const toDoState = atom<IToDo[]>({
key: "toDo",
default: [],
});
<ToDo.tsx>
import { IToDo } from "./atoms";
function ToDo({text}:IToDo) {
return <li>{text}</li>;
}
export default ToDo;
실행결과
li 추가되는 방향은 아래로 바꿨음
따로 파일을 분리시켜도
정상적으로 작동이 잘 되는 것을 확인 할 수 있음