ThemeProvider을
App.tsx → index.tsx로 이동시킨다.(state을 이용하기 위해서)
npm install recoil
⭐️ index.tsx로 가서 render을 RecoilRoot태그로 감싸자.
index.tsx
root.render(
<>
<RecoilRoot>
<ThemeProvider theme={darkTheme}>
<App />
</ThemeProvider>
</RecoilRoot>
</>
);
DarkMode
: App.tsx에서 function을 Router로 보내고
-> 그 다음 Router에서 Coins로 보내고 있다.
App.tsx. 에서 isDark, modifierFunction 이 두 개를
Router -> Coins로 한번 보내고
Router -> Coin -> Chart로 한번더 보낸다.
StateManagement(Recoil) 사용 전
isDark : App -> Router -> Coin -> Chart :: 하나하나 내려줘야함
StateManagement(Recoil) 사용 후
Header -> (isDark) <- Chart :: 필요한 애들이 가져다 쓰면 된다.
atom은 두가지 Argument을 받는다.
1번째는 key값 - 이름으로 유일
2번째는 기본값
// theme.ts 파일
import { DefaultTheme } from "styled-components";
export const darkTheme: DefaultTheme = {
bgColor: "#2f3640",
textColor: "white",
accentColor: "#9c88ff",
cardBgColor: "transparent",
};
export const lightTheme: DefaultTheme = {
bgColor: "whitesmoke",
textColor: "black",
accentColor: "#9c88ff",
cardBgColor: "white",
};
// atoms.ts 파일
export const isDarkAtom = atom({
key: "isDark",
default: false,
})
// App.tsx 파일
function App() {
const isDark = useRecoilValue(isDarkAtom);
return (
<>
<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
<GlobalStyle />
<Router />
<ReactQueryDevtools initialIsOpen={true} />
</ThemeProvider>
</>
);
}
// Coins.tsx 파일 : 사용하는 곳
function Coins() {
const setDarkAtom = useSetRecoilState(isDarkAtom);
const toggleDarkAtom = () => setDarkAtom(prev => !prev);
<button onClick={toggleDarkAtom}>Toggle Mode</button>
}
useRecoilValue
: Recoil의 현재 값을 가져올 수 있다.
import { useRecoilValue } from 'recoil';
import { myState } from './recoilState'; // Recoil 상태를 정의한 파일
function MyComponent() {
const myValue = useRecoilValue(myState);
return (
<div>
<p>My Value: {myValue}</p>
</div>
);
}
useRecoilState
: Recoil 상태 값을 조회하고 변경하기 위해 사용 - 배열 형태, 1번째 요소 Recoil상태의 현재 값, 2번째 요소는 Recoil의 상태를 변경하는 함수
import { useRecoilState } from 'recoil';
import { myState } from './recoilState'; // Recoil 상태를 정의한 파일
function MyComponent() {
const [myValue, setMyValue] = useRecoilState(myState);
const handleButtonClick = () => {
// Recoil 상태인 myState 값을 변경
setMyValue('New Value');
};
return (
<div>
<p>My Value: {myValue}</p>
<button onClick={handleButtonClick}>Change State</button>
</div>
);
}
const [todo, setTodo] = useState("");
const onChange = (event:React.FormEvent<HTMLInputElement>) => {
// TypeScrip 사용하기 때문에, event에 대해 가르쳐줘야함.
const {
currentTarget: { value },
} = event;
setValue(value);
};
const onSubmit = (event:React.FormEvent<HTMLFormElement>) =>{
event.preventDefault();
console.log(todo);
};
return (
<div>
<form onSubmit={onSubmit}>
<input onChange={onChange} value={todo} placeholder="Write To Do" />
<button>Add</button>
</form>
</div>
);
이제 이 부분을 react-hook-form
을 이용해서 바꿀 것이다.
폼이 크다면 Validation
또한 많아야 할 것이다.
그런데 react-hook-form
을 사용하지 않는다면, form에 많은 state들을 등록하게 될 것이다.
npm install react-hook-form
React-Hook-Form에서 문자열을 리턴하는 것은 에러 메시지를 리턴하는 것이다.
register
: 해당 컨트롤의 값을 수집하고, 유효성 검사 수행이 가능하며(onBlur, onChange, state 같은 것을 제공),console.log(register("문자열"));
<input
{...register("firstName",
{ required: "firstName is required" })}
placeholder="firstName"
/>
watch
: Form의 입력값들의 변화를 관찰할 수 있게 해주는 함수console.log(watch());
import React from 'react';
import { useForm } from 'react-hook-form';
function MyForm() {
const { register, handleSubmit, watch } = useForm();
const onSubmit = (data) => {
// 폼 데이터 처리
console.log(data);
};
// watch를 이용하여 특정 폼 컨트롤 값 가져오기
const usernameValue = watch('username');
return (
<form onSubmit={handleSubmit(onSubmit)}>
{/* 폼 컨트롤 등록 */}
<input type="text" {...register('username')} />
<input type="password" {...register('password')} />
<button type="submit">Submit</button>
{/* 특정 폼 컨트롤 값 출력 */}
<p>Username: {usernameValue}</p>
</form>
);
}
handleSubmit
: validation을 담당한다.formState
: 폼의 상태 확인, 상태에 따른 UI조작하거나 사용자에게 메시지를 보여주는 다양한 조작이 가능import React from 'react';
import { useForm } from 'react-hook-form';
function MyForm() {
const { register, handleSubmit, formState } = useForm();
const onSubmit = (data) => {
// 폼 데이터 처리
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{/* 폼 컨트롤 등록 */}
<input type="text" {...register('username', { required: true })} />
<input type="password" {...register('password', { required: true })} />
{/* 유효성 검증 에러 메시지 출력 */}
{formState.errors.username && <p>Username is required.</p>}
{formState.errors.password && <p>Password is required.</p>}
<button type="submit">Submit</button>
</form>
);
}
setError
: 유효성 검사를 통과하지 못한 필드들은 에러를 발생시킨다.// SetError
interface IForm{
email: string;
//...
extraError?:string;
}
const onValid = (data: IForm) => {
if(data.password !== data.password1){
setError("password1", {message: "Password are not the same"})
}
setError("extraError", {message: "Server Offline"});
// 이 ExtraError는 특정한 항목에 해당되는 에러가 아니라 ,전체 form에 해당되는 에러이다.
};
Custom Validation
<input
{...register("firstName", {
required: "firstName is required",
noHacker: (value) =>
value.includes("hacker") ? "no hacker allowed" : true,
})}
정규식 테스트 해볼 수 있는 곳
: https://www.regexpal.com
Categories(Todo의 카테고리를 바꿀 수 있게 해주는 기능)
const onClick = (newCategory: ITodo["category"])=>{
console.log("i wanna to",newCategory);
}
return(
<li>
<span>{text}</span>
{category !== "DOING" && (
<button onClick={()=>onClick("TO_DO")}>To Do</button>
)}
)
const onClick=(event: React.MouseEvent<HTMLButtonElement>) =>{
const{
currentTarget: {name},
} = event;
}
return(
<li>
<span>{text}</span>
{category !== "TO_DO" && (
<button name="TO_DO" onClick={onClick}>To Do</button>
)}
)
배열의 원소를 교체하는 원리
const food = ["pizza", "fruit", "kimchi", "pasta"];
const target = 1; // 내가 바꾸고 싶은 배열의 index
food.slice(0, target);
food.slice(target+1)
[...food.slice(0,target),"감",...food.slice(target+1)]
:: state을 입력 받아서 그걸 변형해 반환하는 순수함수를 거쳐 반환된 값
⭐️ selector는 state(atom)를 가져다가 원하는대로 모습을 변형시킬 수 있는 도구이다. (+ atom은 단순히 배열만 준다.)
Todo의 카테고리에 따라서 알맞은 버튼만 보이게 할 것이다.
// atoms.ts 파일
export const todoState = atom<ITodo[]>({
key: "todo",
default: [],
});
export const categoryState = atom<Categories>({
key: "category",
default: Categories.TO_DO,
});
export const todoSelector = selector({
key: "todoSelector",
get: ({ get }) => {
const todos = get(todoState);
const category = get(categoryState);
return todos.filter((todo) => todo.category === category);
},
});
//ToDoList.tsx 파일
function ToDoList(){
const todo = useRecoilValue(todoSelector);
<CreateToDo />
return(
{todo?.map((todoEle) =>(
<ToDo key={todoEle.id} {...todoEle} />
))}
)
}
get function
이 있어야 atom을 받는다.
vanila Js의 filter함수는 배열을 return한다.
selector는 todoState,categoryStat을 바라보고 있기 때문에, 둘 중 하나의 값이 변하면 selector의 값도 변한다.