Recoil을 오랜만에 쓸 때를 대비하기 위한 간단한 포스트를 정리해봤다. 앞으로 Redux를 공부하고 써 볼 예정이기 때문이다. 나중에 다시 Recoil을 쓸 때 러닝 커브를 좀 더 줄여보기 위해 작성해보았다!
recoil에는 여러가지 훅이 있는데 이 훅들에 대해 먼저 정리해본다.
const toDos = useRecoilValue(toDoState);
recoil atom에 저장한 state의 값을 반환한다.
state의 값만 반환하기 때문에 컴포넌트에서 상태를 읽어오기만 하면 될 때 사용하는 훅. 나중에 상태가 업데이트되면 리렌더링된다.
const setToDos = useSetRecoilState(toDoState);
// 업데이트 함수 작성
const addUser = () => {
return setToDo(users => [...users, user])
}
recoil atom에 저장한 state의 값을 업데이트 하기 위한 setter함수를 반환한다.
상태를 변경하기 위해 비동기로 사용될 수 있는 setter함수를 리턴한다.
컴포넌트가 상태값을 가져오지 않고 쓰기(업데이트)만 하려고 할 때 사용한다. 컴포넌트가 값이 바뀔 때 리렌더링을 하기 위해 컴포넌트를 구독하지 않고도 값을 설정할 수 있게 해준다.
const [toDos, setToDos] = useRecoilState(toDoState);
recoil atom에 저장한 state의 값을 업데이트 하기 위한 setter함수를 반환한다.
react의 useEffect훅과 비슷하다. 해당 컴포넌트에서 값을 불러오고 변경하기도 해야할 때 사용한다. 앞서 useRecoilValue와 useSetRecoilState를 합친 것이라고 보면 된다.
const toDoSelector = selector({
key: "toDoSelector",
//get function필수
get: ({ get }) => { //get함수는 option이라는 객체 인자가 필요하며 그 안의 들어있는 get이 필요하다.
const toDos = get(toDoState) // toDos에 toDoState value을 할당할 수 있다. selector는 atom값에 접근 가능!
return toDos.filter(toDo => toDo.category === "TO_DO"); // Array()- TO_DO 카테고리 배열, 다른 아웃풋이 나오게 된다.
},
});
// 아래는 selector을 사용하는 컴포넌트
// selector을 사용하려면 useRecoilValue을 사용한다.
const toDos = useRecoilValue(toDoSelector);
Recoil에서 함수나 파생된 상태를 나타낸다. 주어진 종속성 값 집합에 대해 항상 동일한 값을 반환하는 부작용이 없는 "순수함수"라고 생각하면 된다.
selector을 이용하면 어떤 상태를 가져다가 다른 상태를 만들 수 있다. 굳이 새로운 atom을 만들지 않아도 하나의 atom에서 다르게 변형하여 사용할 수 있는 것이다.
다시 말하면, selector는 atom의 output을 변형시키는 도구다. 그러니까 같은 atom value로 setState와는 다른 값을 리턴할 수 있다. 주의해야 할 점은 순수함수여야 한다는 것이다.
또한 selector하나에 get함수를 이용하여 여러가지 atom상태값들을 전달받을 수 있다.
const toDoState = atom({
key: "toDo",
default: null,
effects: [ // 로컬스토리지 저장 예시
({ setSelf, onSet }) => { // setSelf 함수는 초기값 지정. onSet함수는 값이 변경될 때마다 값을 동기화
const todoStoreKey = "ToDo";
const savedValue = localStorage.getItem(todoStoreKey);
if (savedValue != null) {
setSelf(JSON.parse(savedValue));
}
onSet((newValue, _, isReset) => {
isReset
? localStorage.removeItem(todoStoreKey)
: localStorage.setItem(todoStoreKey, JSON.stringify(newValue));
});
},
],
});
// setSelf 함수는 로컬스토리지에서 값을 불러와 초기값을 지정해주는 역할.
// onSet 함수는 해당 atom값이 변경될 때마다 로컬스토리지와 atom 값을 동기화해주는 역할
Recoil에서 effects는 부수효과를 관리하고 atom을 초기화 또는 동기화하기 위한 API이다.
atom을 이용해 데이터베이스, 로컬 스토리지 등 다른 상태의 로컬 캐시 값을 사용하려면 effect를 사용해야 값이 변경될 때마다 업데이트할 수 있다.
state synchronization(상태 동기화)
atom을 원격 데이터베이스, 로컬 스토리지처럼 다른 상태의 로컬 캐시값으로 사용할 수 있다. 값이 변경될 때마다 해당 store을 구독하고 atom값을 업데이트할 수 있다.
setSelf()
는 그 값으로 atom을 초기화하며 초기 렌더링에 이용된다.
logging(로깅)
특정 atom의 상태 변화를 간단히 기록할 수 있다.
const currentUserIDState = atom({
key: 'CurrentUserID',
default: null,
effects: [
({onSet}) => {
onSet(newID => {
console.debug("Current user ID:", newID);
});
},
],
});
또한 Recoil effects 또한 cleanup의 부수효과를 관리하기 위해 선택적 cleanup핸들러를 리턴할 수도 있다.
const myState = atom({
key: 'MyKey',
default: null,
effects: [
() => {
...effect 1...
return () => ...cleanup effect 1...;
},
() => { ...effect 2... },
],
});
나중에 recoil을 빠르게 쓰기 위해 정리해봤는데 역시 사람은 공부를 하면서 기록을 해야 한다는 것을 느낀다. 정리를 하니까 좀 더 recoil을 잘 이해하게 된 느낌이다. 뭔가 effects 개념에 대해서 어렴풋이 알고 쓰는 느낌이었는데, 이번에 100% 이해했다고 말할 순 없겠지만 setSelf
와 onSet
, trigger
등 주요 개념을 어떻게 사용하는지는 확실히 이해했다!
물론 Recoil은 지금 정리한 훅들과 API 이외에도 정말 유용한 것들이 많다. 하지만 당장 Recoil을 쓴다고 했을 때 정말 알고 있어야 하는 핵심 기능에 대해 정리해보았다. 또 더 알게 된 Recoil 지식이 있다면 틈틈히 정리하고 수정해야겠다!
그럼 이만 총총...
useRecoilState 설명에서
"react의 useEffect훅과 비슷하다." 부분
useEffect 가 아니라 useState 아닌가요?