This Document wrote based Recoil v0.7.6 at 2022.12.30.
1. Concept
전역 상태 관리를 하는 상태 관리 라이브러리이다. key, value를 한 쌍으로 이루어서 사용한다.
React를 만든 메타에서 직접 만든 Library이기 때문에, React답게 유지하려고 만들었다고 한다.
나는 redux(현재까지 좀 더 많이 사용 중인)를 안써봤기 때문에 잘 모르지만, 비교 사용해본 사람들은 redux보다 사용하기에 단순하고 편리하다고 한다.
실제 익혀서 사용까지 매우 금방이었다. (결국은 짬바인가? 얼마되진 않지만 전보다는 쉬워졌다. 오히려 개념을 익히는 게 더 시간이 많이 걸리지 않을까 싶다.)
공식 사이트에 있는 아래 영상을 보면서 개념을 익히는 것도 매우 유용하다.
2. 기본 사용 환경 세팅
RecoilRoot
App 파일에 아래와 같이 부모트리로 RecoilRoot을 넣어준다.
function App() {
return (
<RecoilRoot>
<CharacterCounter />
</RecoilRoot>
);
}
Atoms
아래와 같이 atom.ts 파일을 만들어두고, export 해준다. 그러면 이를 전체 Component 에서 접근해서 사용이 가능해진다.
export const fontSizeState = atom({
key: 'fontSizeState',
default: 14,
});
3. Atom
3-1. useRecoilState
useState처럼 사용하면서 atom의 value를 핸들링한다.
function TextInput() {
const [text, setText] = useRecoilState(fontSizeState);
const onChange = (event) => {
setText(event.target.value);
};
return (
<div>
<input type="text" value={text} onChange={onChange} />
{text}
</div>
);
}
아래 2가지 함수는 useRecoilState 중 한 부분을 담당한다. 하나의 기능만을 사용하고 싶을 때 호출해서 사용하면 된다. (용도에 맞게 사용하는 것이다.)
3-2. useRecoilValue
해당 atom의 value만 가져오는 방법이다. 핸들링은 불가능하다.
3-3. useSetRecoilState
setState와 동일한 역할을 한다. atom의 value에 값을 저장하게 된다.
function TextInput() {
const [text, modFn] = useRecoilState(toDoState);
// const value = useRecoilValue(toDoState);
// const modFn = useSetRecoilState(toDoState);
return (
<div>{text}</div>
);
}
3-4. 활용 예제
// App.tsx
import React from "react";
import ToDoList from "./ToDoList";
const App = () => {
return (
<>
<ToDoList />
</>
);
};
export default App;
Recoil을 사용해서 ToDoList를 매우 깔끔하게 사용하는 것이 가능해진다.
props로 전달할 필요가 없어지는 게 가장 큰 장점인 것 같다. 단점은, 코드의 상호 연결관계가 컴포넌트만 봐가지고는 좀 떨어지는 것처럼 느껴지는 게 단점인 것 같다.
그러나, 작성되는 코드의 구조적 단순성 때문에 적극 사용하게 되는 게 아닐까 싶다.
// ToDoList.tsx
import React from "react";
import { useRecoilValue } from "recoil";
import { toDoState } from "./atoms";
import CreateToDo from "./components/CreateToDo";
import Todo from "./components/Todo";
const ToDoList = () => {
const toDos = useRecoilValue(toDoState);
return (
<div>
<h1>To Dos</h1>
<hr />
<CreateToDo />
{toDos.map((toDo, i) => {
return <Todo key={toDo.id} {...toDo} />;
})}
</div>
);
};
export default ToDoList;
// atoms.tsx
import { atom } from "recoil";
export interface IToDo {
id: number; // Date.now()
text: string;
category: "TO_DO" | "DOING" | "DONE";
}
export const toDoState = atom<IToDo[]>({
key: "toDo",
default: [],
});
// CreateToDo.tsx
import React from "react";
import { useForm } from "react-hook-form";
import { useSetRecoilState } from "recoil";
import { toDoState } from "../atoms";
interface IForm {
toDo: string;
}
const CreateToDo = () => {
const setToDos = useSetRecoilState(toDoState);
const { register, handleSubmit, setValue } = useForm<IForm>();
const handleValid = ({ toDo }: IForm) => {
setToDos(oldToDos => [
{ id: Date.now(), text: toDo, category: "TO_DO" },
...oldToDos,
]);
setValue("toDo", "");
};
return (
<form
style={{
display: "flex",
flexDirection: "column",
}}
onSubmit={handleSubmit(handleValid)}>
<input
{...register("toDo", {
required: "작성 좀 해줘요",
})}
placeholder="write a to do"
/>
<button>저장</button>
</form>
);
};
export default CreateToDo;
// Todo.tsx
import React from "react";
import { IToDo } from "../atoms";
const Todo = ({ text }: IToDo) => {
return (
<li>
<span>{text}</span>
<button>Done</button>
<button>Doing</button>
<button>To do</button>
</li>
);
};
export default Todo;
4. Selector (#)
기본적으로 Selector는(get, set 포함) 순수함수다. 이를 유지하기 위해서 selector라는 개념이 발생한 것이다.
Selector를 이해하고 나면, atom 이라고 명명한 atom 함수가 정말 잘 지은 이름이라는 것을 알게된다.
4-1. get
atom value의 변화에 따라 연동되어 계산해서 return 하는 함수
4-2. set
atom의 value를 새로운 값으로 변경해주는 함수.
코드를 보자.
4-3 활용예제
// App.tsx
import React from "react";
import { useRecoilState } from "recoil";
import { minutesState, hourSelector } from "./atom";
function App() {
const [minutes, setMinutes] = useRecoilState(minutesState);
const [hours, setHours] = useRecoilState(hourSelector); // Selector 부분
const onChangeMinutes = (event: React.FormEvent<HTMLInputElement>) => {
setMinutes(+event.currentTarget.value);
};
const onChangeHours = (event: React.FormEvent<HTMLInputElement>) => {
setHours(+event.currentTarget.value);
};
return (
<div>
<input
value={minutes}
onChange={onChangeMinutes}
type="number"
placeholder="분 입력"
/>
<input
value={hours}
onChange={onChangeHours}
type="number"
placeholder="시 입력"
/>
</div>
);
}
export default App;
// atom.ts
import { atom, selector } from "recoil";
export const minutesState = atom({
key: "minutesState",
default: 0,
});
export const hourSelector = selector({
key: "hours",
get: ({ get }) => {
const minutes = get(minutesState);
return minutes / 60;
},
set: ({ set }, newHour) => {
const minutes = Number(newHour) * 60;
set(minutesState, minutes);
},
});
key는 selector, atom 등을 구분하기 위해서 고유 key가 요구된다.
get은 그대로 계산한 대로 return하는 것이니까 딱히 별다른 이해가 필요하진 않고, (getter)
set은 atom의 value를 바꾸는 것을 말한다. (setter)
위 코드에서는,
1. atom의 value가 바뀌었기 때문에, get은 한 번 더 계산을 하게 되고,
2. 이에 계산된 값이 다시 한번, input value에 적용된다.
열코하쟈.
리코일은 다른 라이브러리에 비에 간단하고, 같은 회사에서 만든 라이브러리라 그런지 리액트스럽다.
이해하기에 크게 어렵지 않다.
공식문서도 짧으니, 대부분 공식문서를 찾아보며, 정확하게 익히도록 하자.
참고
https://recoiljs.org/ko/docs/introduction/core-concepts
https://tech.osci.kr/2022/06/16/recoil-state-management-of-react/
'React & TypeScript > Recoil' 카테고리의 다른 글
[Redux] 왜 쓸까? (0) | 2021.02.17 |
---|---|
[Redux] Redux를 써야 하는 이유? (0) | 2021.02.01 |
댓글