형식은 기존의 useState
와 유사함
const [value, modFn] = useRecoilState(toDoState);
첫번째 항목 : value (never(빈) 타입의 배열)
두번째 항목 : value를 변경하기 위한 함수
💡
useRecoilValue
: state값을 리턴
💡useSetRecoilState
: setter 함수를 리턴
💡useRecoilState
: state, setter 함수를 모두 리턴
const toDoState = atom<IToDo[]>({
key: "toDo",
default: [],
});
interface IToDo {
text: string;
id: number;
category: "TO_DO" | "DOING" | "DONE";
}
-> ToDo를 만들 때 "TO_DO" | "DOING" | "DONE"
이 3개의 string 중 하나만 가져야함
const [toDos, setToDos] = useRecoilState(toDoState);
toDos
→ IToDo
객체로 이루어진 배열
❌) 그냥 냅다 toDos.push()
→ 이러면 리렌더링이 안됨
❌) setToDos(oldToDos ⇒ [oldToDos])
→ 배열 안에 배열을 담아서 리턴
✅) setToDos(oldToDos ⇒ [...oldToDos])
→ 배열 안의 요소
를 리턴
<ul>
{toDos.map((toDo) => (
<li key={toDo.id}>{toDo.text}</li>
))}
</ul>
<ToDoList>
컴포넌트를
<ToDoList>
<CreateToDo>
<ToDo>
로 나누었다
(이건 그냥 분리한 거니까 생략 …)
{toDos.map((toDo) => (
<ToDo text={toDo.text} category={toDo.category} id={toDo.id} />
))}
props를 넘겨줄 때 … 이렇게 긴 코드를
{toDos.map((toDo) => (
<ToDo {...toDo} />
))}
toDos 리스트의 toDo가 <ToDo>
컴포넌트에서 필요한 props와 같은 모양 (같은 IToDo interface)이기 때문에
<ToDo key={toDo.id} {...toDo} />
이렇게 짧게 줄일 수 있다
{toDos.map((toDo) => (
<ToDo key={toDo.id} {...toDo} />
))}
…
: 해당 배열 안의 모든 원소를 풀어놓기const food = [”pizza”, “mango”, “kimchi”, “kimbab”]
const front = [”pizza”]
const back = [”kimchi”, “kimbab”]
const finalpart = […front, “감”, …back”]
<button name="DOING" onClick={onClick}>
Doing
</button>
const {
currentTarget: { name },
} = event;
atom의 output을 변형시키는 도구
Selector는 파생된 state의 일부를 나타낸다.
기존 state를 가져와서, 기존 state를 이용해 새로운 state를 만들어서 반환할 수 있다.
(기존 state를 이용만할 뿐 변형시키지 않는다)
배열에서 조건에 맞지 않는 원소들을 제거한 배열을 return
toDos.filter((toDo) => toDo.category === "TO_DO")
-> category가 TO_DO인 원소들만 담은 배열을 return
export const toDoSelector = selector({
key: "toDoSelector",
get: ({ get }) => {
const toDos = get(toDoState);
return [
toDos.filter((toDo) => toDo.category === "TO_DO"),
toDos.filter((toDo) => toDo.category === "DOING"),
toDos.filter((toDo) => toDo.category === "DONE"),
];
},
});
const [toDo, doing, done] = useRecoilValue(toDoSelector);
배열 안에 순서대로 이름을 지정
<h2>To Do</h2>
<ul>
{toDo.map((toDo) => (
<ToDo key={toDo.id} {...toDo} />
))}
</ul>
<hr />
<h2>Doing</h2>
<ul>
{doing.map((toDo) => (
<ToDo key={toDo.id} {...toDo} />
))}
</ul>
<hr />
<h2>Done</h2>
<ul>
{done.map((toDo) => (
<ToDo key={toDo.id} {...toDo} />
))}
</ul>
그럼 이렇게 간단하게 배열 속의 배열 가져오기 가능!
export const categoryState = atom({
key: 'category',
default: 'TO_DO',
});
<select onInput={onInput}>
<option value="TO_DO">To Do</option>
<option value="DOING">Doing</option>
<option value="DONE">Done</option>
</select>
이제 이 카테고리에 따라 나누기 위해
현재의 값과 값을 수정하는 함수가 필요함 → useRecoilState
const [category, setCategory] = useRecoilState(categoryState);
const toDos = useRecoilValue(toDoSelector);
export const toDoSelector = selector({
key: 'toDoSelector',
get: ({ get }) => {
const toDos = get(toDoState);
const category = get(categoryState);
return toDos.filter((toDo) => toDo.category === category);
},
});
{toDos?.map((toDo) => (
<ToDo key={toDo.id} {...toDo} />
))}
category에 따라서 selector
가 각각의 toDo 배열 반환
열거형으로 이름이 있는 상수들의 집합을 정의할 수 있다
const category = useRecoilValue(categoryState);
export const categoryState = atom<"TO_DO" | "DOING" | "DONE">({
key: 'category',
default: 'TO_DO',
});
-> typescript에게 categoryState가 "TO_DO" | "DOING" | "DONE"
셋 중 하나일 것이라고 알려주기
export enum Categories {
'TO_DO' = 'TO_DO',
'DOING' = 'DOING',
'DONE' = 'DONE',
}
<option value={Categories.TO_DO}>To Do</option>
<option value={Categories.DOING}>Doing</option>
<option value={Categories.DONE}>Done</option>
</select>
-> string 작성시 실수할 확률 줄어든다!
cf) 기존 코드
<select onInput={onInput}>
<option value="TO_DO">To Do</option>
<option value="DOING">Doing</option>
<option value="DONE">Done</option>
</select>
지난주에 recoil없이 투두리스트를 제작해보았는데 props를 넘겨줄 때 엄청나게 헷갈렸던 경험이 있어서 recoil의 중요성이 더 크게 느껴졌다 ... 휴