[Recoil] 리코일로 Axios Hook 구현하기
2020. 7. 11. 15:54ㆍJavaScript
최종 코드는 제일 아래에 있습니다!
Axios Hook
Axios Hook은 다음과 같이 사용한다.
const [{ data, loading, error }, refetch] = useAxios(
'https://some-url.com'
)
응답의 결과가 담기는 data
, 통신 진행 여부를 알려주는 loading
, 요청의 에러를 담는 error
와 요청을 다시 보낼 수 있는 refetch()
메서드가 리턴된다.
리코일에서도 이와 비슷하게 훅처럼 사용할 수 있으면 좋을 것 같아서 몇 가지를 시도해봤다.
Recoli에서는?
먼저 data
를 받는 코드는 다음과 같이 간단하게 구현할 수 있다. (참고: 리코일 공식 문서)
const getUserInfo = selector({
key: 'getUserInfo',
get: async ({ get }) => {
const response = await axios.get('https://some-url.com');
return response.data;
},
});
const CurrentUserInfo = () => {
const userInfo = useRecoilValue(getUserInfo);
return <div>{userInfo}</div>;
}
const MyApp = () => {
return (
<RecoilRoot>
<CurrentUserInfo />
</RecoilRoot>
);
}
loading
과 error
는 리코일에서 제공하는 Loadable
객체를 사용해서 구현할 수 있다.
...
const CurrentUserInfo = () => {
const userInfoLoadable = useRecoilLoadableValue(getUserInfo);
switch (userInfoLoadable.state) {
case 'hasValue':
return <div>{userInfoLoadable.contents}</div>;
case 'loading':
return <div>loading...</div> ;
case 'hasError':
throw userInfoLoadable.contents;
}
};
...
useRecoilLoadableValue
훅은 useAxios
훅의 {data, loading, error}
와 유사한 결과를 리턴한다. 이 훅은 두 개의 인터페이스를 다음과 같이 갖는다.
state
- 'hasValue', 'loading', 'hasError' 중 하나의 값을 갖는다. 의미가 매우 직관적이다.
contents
state
가 'hasValue'일 경우, 실제 값을 갖는다.state
가 'loading일 경우, 값의
Promise`를 갖는다.state
가 'hasError'일 경우,Error
객체를 갖는다.
Refetch(Refresh)
아직 이 코드는 컴포넌트가 처음 랜더링 될 때만 axios 요청을 보낸다. 만약 임의로 refresh하고 싶다면 다른 방법으로 구현해야한다. 아직 공식적으로 지원하는 기능이 없는 것으로 알고 있다. 하지만 약간의 편법을 동원한다면 이를 구현할 수 있다.selector
는 구독 중인 atom
의 상태가 변할 때마다 값을 새로 계산한다는 점을 이용하는 것이 핵심이다.
const _getUserInfoTrigger = atom({
key: '_getUserInfoTrigger',
default: 0,
});
const getUserInfo = selector({
key: 'getUserInfo',
get: async ({ get }) => {
get(_getUserInfoTrigger); // 트리거의 상태를 구독해서 트리거 값이 변경될 때마다 새로 계산
const response = await axios.get('https://some-url.com');
return response.data;
},
set: ({ set }) => {
// setter가 호출되면 트리거의 값을 1만큼 증가
// => 트리거 값 변경으로 인해 userInfo가 갱신됨
set(_getUserInfoTrigger, v => v + 1);
}
});
const CurrentUserInfo = () => {
const [userInfoLoadable, refetchUserInfo] = useRecoilLoadableState(getUserInfo);
switch (userInfoLoadable.state) {
case 'hasValue':
return (
<>
<div onPress={refetchUserInfo}>리로드</div>
<div>{userInfoLoadable.contents}</div>;
<>
);
...
}
};
위와 같은 방식으로 refetch를 구현할 수 있다.