예를 보면서 설명하도록 하겠습니다.
비동기 통신을 하는 selector를 만들었습니다.
import { selector } from 'recoil';
export const reviewCount = selector({
key: 'asyncState',
get: async () => {
const response = await getErrorReview()
return response
}
});
이제 해당 selector의 값을 보여주는 컴포넌트를 작성해보도록 하겠습니다.
import { useRecoilValue } from 'recoil';
import { reviewCount } from '/store/count';
function RecoilStarCount() {
const reviewList = useRecoilValue(reviewCount);
return (
<>
<p>{reviewList}</p>
</>
);
}
export default RecoilStarCount;
문제가 없어보이지만 이대로 실행하게되면 에러가 발생합니다.
recoil 은 비동기 상태에 대한 처리를 React의 Suspense
를 통해 지원하고 있습니다.
때문에 비동기통신을 사용하는 selector 를 사용하기 위해선 해당 컴포넌트를 Suspense 로 비동기 상태에 대한 처리를 진행해 주어야 합니다. 이제 다음과 같이 Suspense
로 감싼 이후 다시 실행해 보도록 하겠습니다.
<React.Suspense fallback={<div>로딩중입니다.</div>} >
<RecoilStarCount />
</React.Suspense>
Suspense를 사용하면 컴포넌트가 렌더링되기 전까지 기다릴 수 있습니다.
공식문서 를 확인하면 자세한 정보를 볼 수 있습니다.
React.lazy
위의 suspense 를 사용하지 않고 비동기를 제어 해야 하는 경우도 생기기 때문에 이를 위해 Recoil 에서는 useRecoilValueLoadable
와 useRecoilStateLoadable
함수를 지원합니다.
둘의 차이는 useRecoilState
, useRecoilValue
와 같이 setter 포함 유무의 차이입니다. 해당 함수로 호출하면 다음과 같이 2개의 값을 반환하게 됩니다.
Suspense로 감싼 부분을 삭제하고 RecoilStarCount
컴포넌트를 다음과 같이 작성하도록 하겠습니다.
import { useRecoilValueLoadable } from 'recoil';
import { reviewCount } from '/store/count';
function RecoilStarCount() {
const reviewList = useRecoilValueLoadable(reviewCount);
// 로딩 상태 처리
if (recoilStarCount.state === 'loading') {
return <div>loading</div>
}
return (
<>
<p>{reviewList.contents}개</p>
</>
);
}
export default RecoilStarCount;
위의 코드로 변경후 실행하면 suspense로 작업한 결과와 동일한 실행결과를 보실 수 있습니다.
recoil을 통한 비동기 통신의 큰 장점으로는 rudux 에서는 각 state 에 대한 비동기 상태를 별도로 가져야 하지만 recoil 의 selector 는 비동기 상태에 대한 값의 정보는 담고 있지 않아도 useRecoilXXXLoadable
에서 지원해주기 때문에 훨씬 깔끔하고 코드양이 적어지는 큰 장점이 있습니다.