[React] Recoil

hyeondoonge·2023년 6월 12일
1

Recoil?

리액트에서 전역상태를 관리하기위한 전역상태관리 라이브러리이다.

전역상태를 useState이나 ContextAPI로 관리한다면?

👿 전역상태관리 with useState

  • Props drilling
  • 공통 부모에 대한 서브 트리 리렌더링

👿 전역상태관리 with ContextAPI

  • 단일값 저장 (비효율적인 렌더링을 유발하거나 boilderplate를 양산)
  • 비효율적인 리렌더링 (효율적으로 렌더링하도록 개선할 수 있지만 복잡한 구현이 요구됨)

Recoil 사용시 어떤 이점이 있을까?

✓ boilerplate-free
✓ React의 신기능과 호환 (suspense, concurrent mode 등)
✓ 파생 데이터 관리
✓ 컴포넌트 - 상태 사이의 관심사 분리
✓ 비동기 상태 관리
✓ 캐싱

사용방법

recoil에서 가장 코어한 개념인 atom과 selector을 알아보자.

Atom

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)

Selector

파생된 상태를 표현한다. 다른 상태에 의존하는 상태로서 정의할 수 있는데, 이는 의존하고있는 상태가 변경됨에 따라 자동으로 상태가 변경되는 것을 의미한다.

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만 사용가능하다.

✨ Advanced

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()
  })
});

참고

Recoil 공식문서

0개의 댓글