코드를 분리하는걸 리팩토링이라고 한다.
앞선 ToDoList를
Create ToDo와 TodoList 와 ToDo로 나눠보자
이 때 atom을 이용해야 하는데,
atom 에 toDoState를 옮긴다. interface와 함께..export 도 적는다.
import React from "react";
import { useForm } from "react-hook-form";
export default function ToDoList() {
interface IForm {
email: string;
firstName: string;
lastName: string;
username: string;
password: string;
passwordConfirm: string;
extraError?: string;
}
const {
register,
handleSubmit,
formState: { errors },
setError,
} = useForm<IForm>({
defaultValues: {
email: "@naver.com",
},
});
const onValid = (data: IForm) => {
if (data.password !== data.passwordConfirm) {
setError("passwordConfirm", { message: "Password is not same" });
}
// setError("extraError", { message: "server offline" }, { shouldFocus: true });
};
console.log(errors);
return (
<div>
<form style={{ display: "flex", flexDirection: "column" }} onSubmit={handleSubmit(onValid)}>
<input
{...register("email", {
required: "email is required",
pattern: {
value: /^[A-Za-z0-9._%+-]+@naver.com$/,
// ^ :문장의 시작
// [] : 문자셋 안의 아무문자
// + : 하나 또는 많이
message: "Only naver.com emails allowed",
},
})}
placeholder="Email"
/>
<input
{...register("firstName", {
required: true,
minLength: 3,
validate: {
NoEugene: (value) => (value.includes("eugene") ? "No Eugene allowed" : true),
NoEugenius: (value) => (value.includes("eugeniusS") ? "No Eugene allowed" : true),
},
})}
placeholder="First Name"
/>
<span>{errors.firstName?.message}</span>
<input
{...register("lastName", {
required: true,
minLength: {
value: 5,
message: "Your password is too short",
},
})}
placeholder="Last Name"
/>
<span>{errors?.lastName?.message}</span>
<span>{errors?.email?.message}</span>
<input {...register("password", { required: true })} placeholder="Password" />
<span>{errors?.password?.message}</span>
<input {...register("passwordConfirm", { required: "Password is required" })} placeholder="Password Confirm" />
<span>{errors?.passwordConfirm?.message}</span>
<button>Add</button>S<span>{errors?.extraError?.message}</span>
</form>
</div>
);
}
import { createGlobalStyle } from "styled-components";
import ToDoList from "./components/07_Refactoring_ToDoList";
const GlobalStyle = createGlobalStyle`
@import url('https://fonts.googleapis.com/css2?family=Archivo+Narrow:wght@500&family=Bebas+Neue&family=Black+Han+Sans&family=Do+Hyeon&family=Source+Sans+Pro:wght@300;400&family=Ubuntu+Mono:ital@1&display=swap');
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, menu, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
main, menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, main, menu, nav, section {
display: block;
}
/* HTML5 hidden-attribute fix for newer browsers */
*[hidden] {
display: none;
}
body {
line-height: 1;
}
menu, ol, ul, li {
list-style: none;
}
button{
border: none;
outline: none;
background-color: transparent;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
*{
box-sizing: border-box;
}
body{
font-family: 'Source Sans Pro', sans-serif;
}
a{
text-decoration: none;
color:inherit;
}
`;
export default function App() {
return (
<>
<GlobalStyle />
<ToDoList />
</>
);
}
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, id: Date.now(), category: "TO_DO" }, ...oldToDos]);
setValue("toDo", "");
};
return (
<form onSubmit={handleSubmit(handleValid)}>
<input
{...register("toDo", {
required: "Please write a To Do",
})}
placeholder="Write a to do"
/>
<button>Add</button>
</form>
);
}
export default CreateToDo;
import { atom } from "recoil";
export interface IToDo {
text: string;
id: number;
category: "TO_DO" | "DOING" | "DONE"; //string;
//위 문자열만 받도록 제한한다.
}
export const toDoState = atom<IToDo[]>({
//atom 타입이 toDo의 배열임을 알려준다.
key: "toDo",
default: [],
});