리액트 앱 테스트를 공부하던 나 . . 더미 데이터로 개발하고 있었지만 비동기 테스트 부분을 배울 차례가 되어 더미 데이터가 아닌 서버 측 데이터를 fetch 하도록 코드 변경이 필요했다.
빠른 개발을 위해 가장 간단한 방법을 선택했다. Recoil 은 상당히 리액트스러워서 default 값만 서버 데이터로 변경하면 코드 변경이 거의 없다. 솔루션은 다음과 같다.
(기존) 더미 데이터 사용
실제로는 useState
를 사용했지만 비교를 위해 Recoil로 구현해 봤다.
import { atom } from 'recoil';
export const myAtom = atom({
key: 'myAtom',
default: DUMMY_DATA,
});
(변경) API 데이터 사용
import { atom, selector } from 'recoil';
import { getData } from '../api';
export const mySelector = selector({
key: 'mySelector',
get: async () => {
return await getData();
},
});
export const myAtom = atom({
key: 'myAtom',
default: mySelector,
});
위처럼 비동기 데이터를 atom의 default 값으로 설정할 수 있었다. 하지만 다음과 같은 에러가 발생했다.
Warning: Can't perform a React state update on a component that hasn't mounted yet. This indicates that you have a side-effect in your render function that asynchronously later calls tries to update the component. Move this work to useEffect instead.
마운트 되지 않은 컴포넌트의 state 를 업데이트 할 수 없다는 문제였다. 즉 useEffect를 사용해서 컴포넌트가 마운트 되면 그 후에 해당 state를 업데이트 해주라는 것이다. 그러면 Recoil 안 썼지 ..
<React.Suspense> 를 사용하자!
해당 문제는 공식 문서에도 잘 나와 있는 내용이라 쉽게 해결할 수 있었는데 비동기 데이터를 사용하는 경우 React.Suspense 를 사용해 fallback 을 지정해 주면 된다.
비동기 데이터를 사용하는 컴포넌트
export default function MyComponent() {
const [data, setData] = useRecoilState(myAtom);
return (
<div>
<h1>{data.title}</h1>
<p>{data.text}</p>
<button>{() => setData({ title: 'new', text: 'new' })}
새로운 데이터로 변경
</button>
</div>
);
}
위 컴포넌트를 감싸는 컴포넌트
export default function MyApp() {
return (
<RecoilRoot>
<React.Suspense fallback={null}>
<MyComponent />
</React.Suspense>
</RecoilRoot>
);
}
비동기 데이터가 도착하기 전까지 아무것도 보여주고 싶지 않아서 fallback 을 null 로 지정하였는데, 보여주고 싶은 특정 컴포넌트가 있다면 해당 컴포넌트로 지정하면 된다.
참고로 selector 내부에서 발생하는 에러를 catch 하고 싶다면 다음과 같이 ErrorBoundary로 컴포넌트를 감싸면 된다.
export default function MyApp() {
return (
<RecoilRoot>
<ErrorBoundary fallback={<p>something went wrong..</p}>
<React.Suspense fallback={null}>
<MyComponent />
</React.Suspense>
</ErrorBoundary>
</RecoilRoot>
);
}
위 코드는 설명을 위해 임의로 작성하였는데, 실제 코드 및 관련 커밋이 궁금하다면 아래 링크를 확인해 주십쇼