
#6.0 ~ 6.2 (2023.04.12 수)
이번 섹션에서는 Recoil을 이용해서 state management하는 법을 배워보자.
Recoile은 페이스북에서 만든 React Js에서 사용할 수 있는 state management임. 미니멀하고 간단해서 이해하기 쉽고 쓰기 쉬움.
우선 state management가 왜 필요한지 이해하기 위해 state management를 사용하지 않고 코인 어플리케이션에 다크모드/라이트모드가 되는 toggle 버튼을 만들어 볼거임.

index.tsx 파일에 있던 <ThemeProvider/>을 App.tsx 파일로 옮겨줌.

theme.ts로 가서 원래있던 theme을 darkTheme으로 바꾸고 반대로 lightTheme도 만들어줌.App.tsx에 다시가서 isDark state를 만들어 누를때마다 false <-> true가 되도록 함. 결과 화면 ↓

ThemeProvider를 index에서 App으로 옮긴 이유는 state를 사용하려고 옮긴것!
토클 버튼을 홈페이지의 제목옆의 위치로 옮기고 싶어서 App.tsx에 있는<button/>을 Coins.tsx에 옮겨줌.


일단 state management를 사용하지 않고 props 만으로 제목옆의 토글버튼을 클릭해서 다크모드를 끄고 킬 수 있게 바꿔보자.

Coins.tsx에서 사용하기 위해
() => void; : argument를 받지 않고 아무것도 return하지 않는다는 의미이번엔 chart에게 다크모드인지 아닌지 isDark state를 props로 내려줘야함.



//props 내려준 과정
App (isDark, modifierFn)
App-> Router -> Coins (modifier)
App-> Router -> coin -> Chart (isDark)
위에서 작업한 props를 내리는 과정은 너무 길고 효율적이지 않았음.
💡 state들을 따로 모아놓고 원하면 가져올 수 있는 state management가 필요한 이유임!
//props 내려준 과정
App (isDark, modifierFn)
App-> Router -> Coins (modifier)
App-> Router -> coin -> Chart (isDark)
리코일은 이해하기 쉬운 state management임. 공식홈페이지의 영상을 참고하기!
function App() {
// const [isDark, setIsDark] = useState(false);
// const toggleDark = () => setIsDark((current) => !current);
return (
<>
<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
{/* <button onClick={toggleDark}>Toggle Dark Mode</button> */}
<GlobalStyle></GlobalStyle>
<Router></Router>
<ReactQueryDevtools initialIsOpen={true} /> {/* 2 */}
</ThemeProvider>
</>
);
}
export default App;
npm install recoil
1. 위의 명령어로 리코일을 설치함.
//index.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { QueryClient, QueryClientProvider } from "react-query";
import { RecoilRoot } from "recoil"; // 1. import 해오기
import App from "./App";
const queryClient = new QueryClient();
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<div>
<RecoilRoot> {/* 1 */}
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</RecoilRoot>
</div>
);
<RecoilRoot/> 감싸주자. QueryClientProvider과 비슷함.시작하는 방법은 여기까지가 다임. 매우 간단함!
//src/atoms.ts
import { atom } from "recoil";
export const isDarkAtom = atom({
key: "isDark",
default: true,
});
src/atoms.ts 파일을 생성함. 여기서는 리코일의 atom function을 사용해야함.이제 atom이 필요한 App과 Chart 컴포넌트에 atom의 value랑 연결해보자.


useRecoilValue() 훅에 isDarkAtom을 넣으면 isDark를 사용할 수 있음.
위와 같은 순서대로 하면 라이트/다크모드가 잘 구현됨.
✔️결과적으로 App과 Chart 컴포넌트에만 Atom의 value와 연결해서 사용한것임!
#6.3 ~ 6.6 (2023.04.13 목)
함수를 사용하기 위해서 atom의 value를 어떻게 수정하는지 배워보자.


useRecoilValue라는 hook을 쓰지만 atom의 value를 수정하기 위해서는 useSetRecoilState라는 hook을 씀useSetRecoilStat는 function을 가져오는데 이 function이 value를 수정할거임
리코일로 다크모드/라이트모드 구현결과 ↓

리코일을 사용한 todo-list를 만들기 위해 세팅해주자!
npx create-react-app myApp --template typescript
npm i --save-dev @types/styled-components
npm i styled-components
npm i recoil
기존의 코인 어플리케이션 폴더에서 만들어도 상관없지만 새로 만들고 싶다면 위와 같이 세팅해주면 됨. (나는 위의 명령어들로 폴더를 새로 만들고 코인 어플리케이션의 코드를 붙여넣어줌.)

위와 같은 환경에서 시작함.(리액트,타입스크립트,리코일)


App.tsx,index.tsx,theme,styled.d.ts 파일만 남겨줌.App.tsx에 기존의 GlobalStyle을 붙여넣기 해줌.이러면 세팅은 끝!
이제 recoil의 다른 함수와 기능들에 대해 배워보자. atoms 말고도, 배우면 좋은 다른 기능들이 많음.
//App.tsx
import { createGlobalStyle } from "styled-components";
import ToDoList from "./ToDoList";
const GlobalStyle = createGlobalStyle`
//코드생략
`;
function App() {
return (
<>
<GlobalStyle />
<ToDoList />
</>
);
}
export default App;

event: React.FormEvent<HTMLInputElement> 이 속성은 타입스크립트를 사용할 때만 필요함.const { currentTarget: { value },} = event; : 이 문법은 es6문법으로 const value = event.currentTarget.value 와 같은 뜻.이번 강의에서는 react-hook-form에 대해 배워보자.
react-hook-form은 모든 걸 쉽게 만들어줘서 리액트에서 form으로 작업하기에 가장 좋은 방법임. 다양한 form 관리 방법과 라이브러리들 중에 최고라고 할 수 있음. 큰 form들과 form validation이 많을 때도 유용함.

react-hook-form은 위의 모든 걸 한 줄의 코드로 대체 가능!
실제 앱을 빌드를 한다고 가정해볼때, user가 계정을 생성하거나 회원가입을 하면 당연히 수많은 input들을 가지게 될건데(이메일,이름,성,비밀번호,비밀번호 확인창 등등) react-hook-form을 사용하지 않는다면 아래와 같이 form에 수많은 state 들을 가져야 할거임.

게다가 큰 규모의 앱을 만든다면 form validation이 필요할 거임.

하나의 항목도 이 정도로 해야 하는데 이름,성,아이디,이메일,비밀번호 등등 input창이 여러 개면 직접 하는것이 매우 번거로울 거임.
npm install react-hook-form
위의 명령어로 react-hook-form을 설치해주자.
//ToDoList.tsx
// import React, { useState } from "react";
import { useForm } from "react-hook-form";
// function ToDoList() {
// const [toDo, setToDo] = useState("");
// const [toDoError, setToDoError] = useState("");
// const onChange = (event: React.FormEvent<HTMLInputElement>) => {
// const {
// currentTarget: { value },
// } = event;
// setToDoError("");
// setToDo(value);
// };
// const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
// event?.preventDefault();
// if (toDo.length < 10) {
// return setToDoError("To do should be longer");
// }
// console.log("submit");
// };
// return (
// <div>
// <form onSubmit={onSubmit}>
// <input onChange={onChange} value={toDo} placeholder="Write a to do" />
// <button>Add</button>
// {toDoError !== "" ? toDoError : null}
// </form>
// </div>
// );
//
function ToDoList() {
const { register, watch } = useForm();
return (
<div>
<form>
<input placeholder="Write a to do" />
<button>Add</button>
</form>
</div>
);
}
export default ToDoList;
register하는 함수를 사용하면 onChange 이벤트 핸들러와 input의 props가 필요없음. (useState도 필요 없어짐)
console.log(register("todo"))를 하면 onChange, onBlur이벤트 등을 return하고 있고, register 함수에는 문자열을 보내줘야 name에 할당됨.//ToDoList.tsx
function ToDoList() {
const { register, watch } = useForm();
return (
<div>
<form>
<input {...register("todo")} placeholder="Write a to do" />
<button>Add</button>
</form>
</div>
);
}
export default ToDoList;
이제 작동하는지 알아보기 위해 useForm 함수의 watch를 사용해 보자.
watch는 form의 입력값들의 변화를 추적할 수 있게 해주는 함수임

console.log(watch())를 하면 toDo를 키값으로, input의 적은 입력값을 value를 가지고 있는 객체를 리턴함.
const { register, watch } = useForm(); 단 한줄의 코드로 원래의 코드를 대체함!#6.7 ~ 6.9 (2023.04.14 금)

위의 form의 onSubmit 이벤트를 리액트 훅 폼으로 대체해 보자.

handleSubmit이란 함수를 받아와야 함. handleSubmit은 validation 뿐만 아니라 이벤트를 preventDefault 하는 것도 담당함.handleSubmit을 호출하게 하고 handle Submit은 2개의 인자를 받음. handleSubmit(): 하나는 데이터가 유효할 때 호출하는 함수(onValid)고, 다른 하나는 데이터가 유효하지 않을 때 호출되는 함수(onInvalid)임.정리: 유저가 submit을 하면 handleSubmit은 해야하는 모든 validation이나, 다른 일들을 전부 끝마친 후에 데이터가 유효할 때만 onValid 함수를 호출함.
위와 같이 하면 onSubmit 이벤트가 대체됨. 이제 required 속성을 넣어보자
//ToDoList.tsx
function ToDoList() {
const { register, handleSubmit } = useForm();
const onValid = (data: any) => {
console.log(data);
};
return (
<div>
<form onSubmit={handleSubmit(onValid)}>
<input
{...register("email", { required: true, minLength: 10 })}
placeholder="email"
/>
<input
{...register("firstName", { required: true })}
placeholder="firstName"
/>
<input
{...register("lastName", { required: true })}
placeholder="lastName"
/>
<input
{...register("userName", { required: true, minLength: 10 })}
placeholder="username"
/>
<input
{...register("password", { required: true, minLength: 5 })}
placeholder="password"
/>
<input
{...register("password1", { required: true, minLength: 5 })}
placeholder="password1"
/>
<button>Add</button>
</form>
</div>
);
}
원래는 HTML태그에 required 속성을 넣어서 (ex:<input required/>) 작동하게 할 수 있지만, 유저가 지원하지 않는 브라우저나 모바일에서 본다거나 혹은 브라우저의 소스코드를 수정할 수 있는 위험이 있음.
-> HTML에 의지하는 대신, 위와 같이 자바스크립트에서 validation을 하기위해 register 함수 안에다가, {required: true}를 넣어줌.

{required: true}로 인해 입력하지 않은 input으로 커서를 자동으로 옮겨줌. 이것이 리액트 훅 폼의 장점!
-> 이걸 직접 스스로 만든다면 어떤 항목이 비어있는지 알아야 하고, 비어있는 첫항목이 어딘지 알아내서 사용자를 거기로 옮겨야 할거임.
minLength: 5 는 if(toDo.length < 10) 조건문을 대신해줌
이제 useForm에 formState라는 프로퍼티를 하나 더 받아보자!

이 프로퍼티의 에러를 console에 찍어보면 에러의 type을 알려줌.


이제 에러를 유저들에게 보일 수 있게 해보자!
function ToDoList() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
useForm에 있던 formState를 위와 같이 바꿔주자.


{errors?.email?.message as string} 로 고쳐줌.정규표현식
^ 문장의 시작
/^[A-Za-z0-9._%+-]+@naver.com$/
/^[A-Za-z0-9._%+-]+@naver.com/g
A-Z: A부터 Z까지의 대문자a-z: a부터 z까지의 소문자 0-9._%+-: 숫자와 부호들 전부
모든 input밑에 span을 붙여 넣어주면 이제 모든 input창 밑에 에러메시지가 뜨게 하는거 완성!
지금은 data를 any라고 했는데, 타입스크립트에게 any라고 알려주는 건 좋지 않기 때문에 타입스크립트에게 form이 모양을 interface로 알려줘보자.

defaultValues옵션: 미리 기본값을 설정할 수 있음어떤 경우에는 서버에서 문제가 발생해서 에러를 발생시켜야 할 경우도 있어서 에러를 어떻게 발생시킬 수 있는지를 알아보자.
그리고 지금 까지 배운 검사 방법(minLength, required, 정규식) 이외의 원하는 규칙에 따라 만들 수 있는 validation 방법을 배워보자.
예를 들어 유저가 username을 적는 도중에 API에 요청을 보내서 사용자명이 이미 존재하는지 확인한다음 존재한다면, 사용자에게 즉시 화면에 알려줄 수 있음.
이런건 react-hook-form으로 개발자가 만든 규칙에 따라 검사하는 것을 만들 수 있기 때문에 가능함.


사용자가 form을 제출했는데 서버가 다운되서 접속이 끊겼을때 같은 상황에 사용 가능함.

extraError를 넣어줌.extraError: 특정한 항목에 해당하는 에러가 아니라, 전체 form에 해당되는 에러이름이 nico거나 이름에 nico를 포함하고 있는 유저 가입을 막는 상황을 만들어 보자.

validate 옵션은 함수를 값으로 가지고 인자로 항목에 현재 쓰여지고 있는 값을 받음.validate 옵션은 하나의 함수 또는 여러 함수가 있는 객체가 될 수 있음. -> input에 여러 개의 검사가 필요할 수 있기 때문validate 함수를 async로 비동기로 만들어서 서버에다가 확인하고 응답을 받을 수도 있음.