Recoil 비동기처리를 위한 훅 만들기

marrywill·2022년 1월 30일
0

커스텀훅

목록 보기
1/1

리코일에서의 비동기요청 처리 방식

Recoil에서 비동기 데이터를 처리하는 방식은 여러가지가 있습니다.

1. useRecoil훅

비동기 처리 예시

const currentUserNameQuery = selector({
  key: 'CurrentUserName',
  get: async ({get}) => {
    const response = await myDBQuery({
      userID: get(currentUserIDState),
    });
    return response.name;
  },
});
function CurrentUserInfo() {
  const userName = useRecoilValue(currentUserNameQuery);
  return <div>{userName}</div>;
}

위의 코드가 가장 간단한 예시인데요. useRecoilValue, useRecoilState이 비동기 selector를 이용하여 데이터를 불러오는 방식입니다. 이를 처리하기 위해서는 해당 컴포넌트의 데이터가 처리되기 전의 로딩 처리를 위한 Suspense를 동반하게 됩니다. 또한 에러 처리까지 붙이게 되면 에러를 위한 ErrorBoundary까지 추가되어야하는 형태인 것이죠.

해당 방법은 코드적으로 처리해야할 부분이 많아지는 상황이 생기기 때문에 더 좋은 방법을 찾아보도록 합시다.

2. useRecoilValueLoadable

useRecoilValueLoadable 예시

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

useRecoilState와 다르게 이 hook은 비동기 selector에서 읽어올 때 (React Suspense와 함께 작동하기 위해) Error 혹은 Promise를 던지지 않습니다. 대신 이 hook은 Loadable 객체를 setter 콜백과 함께 리턴합니다.

매번 사용시마다 생겼던 Suspense와 ErrorBoundary 처리를 하지않아도 되도록 loading과 error 상태를 관리해주는 훅입니다.리덕스에서 미들웨어를 사용하여 상태관리를 하던 모양과 흡사합니다.

해당 훅도 매번 switch문으로 상태를 봐야하는 상황이기 때문에, 돌려받은 리턴값을 통해 어떠한 상태인지를 알 수 있도록(redux 상태관리와 흡사하게) 커스텀훅을 제작해보았습니다.

useRecoilLoadable 커스텀훅

import { useEffect, useState } from 'react';
import { RecoilValueReadOnly, useRecoilValueLoadable } from 'recoil';

type LoadableObjectState<T> = {
  loading: boolean;
  data: T | null;
  error: any | null;
};

const useRecoilLoadable = <T>({
  selector,
}: {
  selector: RecoilValueReadOnly<T>;
}): LoadableObjectState<T> => {
  const [recoilState, setRecoilState] = useState<LoadableObjectState<T>>({loading: true, data: null, error: null});
  const loadable = useRecoilValueLoadable(selector);
  useEffect(() => {
    switch (loadable.state) {
      case 'hasValue':
        setRecoilState({
          loading: false,
          data: loadable.contents,
          error: null
        });
        break;
      case 'loading':
        setRecoilState({
          loading: true,
          data: null,
          error: null
        });
        break;
      case 'hasError':
        setRecoilState({
          loading: true,
          data: null,
          error: loadable.contents
        });
        break;
      default:
        return;
    }
  }, [loadable, loadable.state]);
  return recoilState;
};
export default useRecoilLoadable;

해당 코드로 비동기 처리를 압축할 수 있어서 도움이 되었지만 리코일에서 제공해주는 캐싱기능의 문제로 새로운 데이터를 매번 갱신해야하는 상황에서는 어려움을 겪게 되었습니다. 리퀘스트 ID를 넘겨 매번 호출마다 증가시킴으로써 필요한 곳에서 매번 새롭게 리패칭하도록 유도하는 방식도 있지만 좀 더 정책상으로 매끄럽게 해결되는게 좋다고 생각이 들었습니다.

리코일자체가 메이저버전이 나오지 않은 터라 그 부분은 점차 개선이 되어질거라 생각합니다.

profile
주니어 프론트엔드

0개의 댓글