리액트에서 전역상태를 관리하기위한 전역상태관리 라이브러리이다.
👿 전역상태관리 with useState
👿 전역상태관리 with ContextAPI
✓ boilerplate-free
✓ React의 신기능과 호환 (suspense, concurrent mode 등)
✓ 파생 데이터 관리
✓ 컴포넌트 - 상태 사이의 관심사 분리
✓ 비동기 상태 관리
✓ 캐싱
recoil에서 가장 코어한 개념인 atom과 selector을 알아보자.
unique key, default value와 함께 정의한다.
상태 읽기 useRecoilValue()
, 상태 쓰기 useSetRecoilState()
hook을 사용한다.
const todoListState = atom({
key: 'TodoList',
default: [
{ name: '밥먹기', isComplete: true },
{ name: '양치하기', isComplete: false }
]});
const todoListFilterState = atom({
key: 'TodoListFilter',
default: 'Show All',
});
// get user state
const todoListFilter = useRecoilValue(todoListFilterState)
파생된 상태를 표현한다. 다른 상태에 의존하는 상태로서 정의할 수 있는데, 이는 의존하고있는 상태가 변경됨에 따라 자동으로 상태가 변경되는 것을 의미한다.
const filteredTodoListState = selector({
key: 'FilteredTodoList',
get: ({get}) => {
const filter = get(todoListFilterState);
const list = get(todoListState);
switch (filter) {
case 'Show Completed':
return list.filter((item) => item.isComplete);
case 'Show Uncompleted':
return list.filter((item) => !item.isComplete);
default:
return list;
}
},
});
const [filter, setFilter] = useRecoilState(todoListFilterState)
selector로 정의한 상태가 todoListFilterState, todoListState상태에 의존하고있다. 두 상태가 변경되면 selector가 업데이트된다.
atom과 동일하게 읽기, 쓰기 hook을 제공한다. 단 atom과는 다르게 쓰기 hook은 get, set모두 정의된 selector만 사용가능하다.
1. 비동기 데이터 쿼리
get 함수가 promise를 반환하게하여 비동기 데이터를 가져올 수 있다.
유니크한 입력에 대해 결과를 cache한다 → read only 데이터를 쿼리할 때 유용하다! (데이터의 성격이 변경가능하다면, Query Refresh를 고려해볼 수 있음)
비동기로 데이터를 가져올 경우 로딩과 에러를 핸들링해야한다. React의 Suspense, Error Boundary 컴포넌트와 함께 조합해서 사용하거나, useRecoilValueLoadable 훅을 이용해서 핸들링할 수 있다.
2. SelectorFamily
selector에 파라미터를 전달하는 방법이다. 컴포넌트 단에서 파라미터 값을 받아 사용할 수 있다.
const userNameQuery = selectorFamily({
key: 'UserName',
get: userID => async () => {
const response = await myDBQuery({ userID });
return response.name;
},
});
function UserInfo({userID}) {
const userName = useRecoilValue(userNameQuery(userID));
return <div>{userName}</div>;
}
3. Atom default
atom의 디폴트 값으로서 사용할 수 있다
export const UserIdState = atom({
key: "UserId",
default: "migu554"
});
export const UserInfoState = atom({
key: "UserInfo",
default: selector({
key: "UserInfo/Default",
get: ({ get }) => getUserInfo()
})
});