Recoil

Eunbi Park·2021년 1월 7일
3
post-thumbnail

이 글은 Deview: Recoil 왕위를 계승하는 중입니다. 이 영상을 기반으로 정리된 글입니다.

Recoil

페이스북에서 개발 한 React 전용 상태관리 라이브러리

  • React 만을 위해 작성된 라이브러리
  • 내부적으로 React API를 가져다 쓰고 있음
  • React 프로젝트에서 사용하기에 간단하고 용이
  • React에 추가 될 Concurrent 모드도 지원 예정
    • Concurrent 모드: 어떤 컴포넌트를 언제 렌더링할지 React가 알아서 최적화를 해준다는 개념:
  • React Hooks로만 사용 가능

Recoil 데이터 흐름

  • 데이터 흐름이 Atoms이 Selector를 따라 흘러서 Component에 도달

Atom

  • Recoil에서 상태 데이터 조각을 정의
  • key와 기본 값(default) 만 정의하면 되는 간단한 구조
const countState = atom({
	key: 'count',
	default: 0,
})

Selector

아톰에서 파생된 데이터 조각.

Selector는 다음과 같은 역할을 한다.

  1. atom에서 파생된 데이터 조각
  2. 데이터를 반환하는 순수 함수

하나의 selector는 두가지 역할을 다 할 수 있기도 하고, 순수 함수 역할만 해도된다.

slector는 {key, get} 구조로 선언한다. 값을 선언하는 역할을 한다면 setter도 넘겨주면 된다.

const oddEvenState = selector({
  key: 'oddEvenState',
  get: ({get}) => {
    const count = get(countState);
    return count %2 ? '홀' : '짝';
  }
})

atom과 다르게 selector에는 default가 없다. atom의 파생데이터이기 때문이다.

selector getter의 인자에는 상태값을 결정하는 객체가 전달 된다. setter의 인자로는 ({get, set, reset})을 받는다.

<RecoilRoot/>

  • 하위의 모든 Recoil 데이터의 root
  • 내부적으로 Context API를 사용
  • Redux에서는 store를 직접만들어야 했지만 Recoil 에서는 직접 만들어 주지 않아도 됨
    • RecoilRoot 를 선언만 해도 자동으로 store생성
  • 중첩 가능 → atom, selector는 가장 가까운 RecoilRoot것을 사용함

Hooks

Recoil Hook은 atom, selector가 공통으로 사용할 수 있다.

useRecoilValue

읽기 전용 Recoil Hook. setter없이 상태값만 반환한다.

 const oddEven = useRecoilValue(oddEvenState);

useRecoilState

값을 구독하여 변경할 수 있다. useState 와 동일한 형태로 [상태, setter]를 반환한다.

const [count, setCount] = useRecoilState(countState);

useSetRecoilState

setter함수만 반환한다.

useResetRecoilState

상태를 기본 값으로 초기화 한다.

useRecoilValueLoadable

비동기 전용 hook.

useRecoilValue랑 유사하게 사용되며 Loadable 객체를 반환한다.

Loadable 객체 안에는 selector getter에서 반환한 상태(.contents)와 비동기 동작의 상태를 포함하고 있다.

Loadable = {
	.state, // 상태 ('hasValue' || 'loading' || 'hasError')
	.contents, // 실제 콘텐츠 (사진 url..)
}

useRecoilStateLoadable

비동기 전용 hook.

useRecoilState와 유사하게 사용되며 Loadable, setter 함수를 반환한다.

Recoil에서 비동기

스피커: 다른 상태 라이브러리보다 비동기를 다루기 수월!

비동기코드를 동기식코드와 거의 유사하게 다룰 수 있고 비동기를 처리하는데 별도의 third-party 라이브러리가 필요 없다.

export const randomImage = selector({
  key: 'randomImage',
  get: async () => {
    const response = await fetch('/api/url');
    const data = await response.json();

    return data.file;
  }
});

< React.Suspense> 로 비동기 데이터를 받는 컴포넌트를 감싸 비동기 작업을 수행할 수 있다.

export default function App() {
  return (
    <div className="App">
      <h1>Random Image</h1>
      <RecoilRoot>
        <React.Suspense fallback={null}>
          <RandomImage />
        </React.Suspense>
      </RecoilRoot>
    </div>
  );
}

React Suspense
컴포넌트를 완전히 렌더링할 수 있게까지 렌더링을 멈추는 컴포넌트.
현재 실험단계로 비동기로 데이터를 받아오는 컴포넌트를 감싸준다.
fallback prop에는 기다리는 동안 보여줄 컴포넌트를 전달한다.

Recoil에서 비동기 데이터는 Loadable이라는 객체로 변환할 수 있다.

Loadable를 사용하려면 useRecoilValue를 비동기 전용 hook으로 바꿔줘야 한다.

Suspense를 처리할 때는 error 바운더리를 사용하거나 selector에서 try catch를 통해 에러 트래킹 필요하지만,

Recoil 비동기 hook 자체 예외를 발생시키지 않는다.

Loadable.state === loading → .contents는 Promise
Loadable.state === hasError → .contents는 Error

function UserInfo({userID}) {
  const userNameLoadable = useRecoilValueLoadable(userNameQuery(userID));
  switch (userNameLoadable.state) {
    case 'hasValue':
      return <div>{userNameLoadable.contents}</div>;
    case 'loading':
      return <div>Loading...</div>;
    case 'hasError':
      throw userNameLoadable.contents;
  }
}

캐싱

selector안에서 사용되는 atom에는 자동으로 의존성이 걸린다.

의존성이 걸린 값(atom) 변결 될 때 마다 파생된 selector가 변경된다.

의존성 걸린 값이 같으면 내부적으로 반환값을 메모이즈 하고 있어 캐싱된 값을 반환한다.

export const animeList = selector({
  key: 'animeList',
  get: async ({ get }) => {
    const keyword = get(keywordState); //keywordState 는 atom
    const response = await fetch(
      `https://api.jikan.moe/v3/search/anime?q=${keyword}&rated=pg13&page=1`
    );
    const data = await response.json();

    return data.results;
  }
});

Recoil 장단점

React와 연계성이 좋음Hooks를 통해서만 사용 가능
사용법이 직관적이고 단순프로덕션 레벨에서 사용하기엔 부담
비동기 데이터 다루기 수월현재는 디버깅 도구 지원 미미

참고하면 좋은 글

https://ui.toast.com/weekly-pick/ko_20200616

https://medium.com/@songc/recoil-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0-14921ad200aa

profile
Front-end developer

0개의 댓글