마켓 플레이스의 정산쪽을 개발하면서, 새로운 기술을 많이 적용했다. 그 중의 하나가 React Query인데, 이녀석을 사용하면서 굉장히 난감한 상황을 마주치게 되었다.
[실제 영상은 대외비라, 코드 샌드박스 예제로 대체하였습니다.]
코드는 간략하게 다음과 같다.
function SalesOfCustomerManagement(props: SalesOfCustomerManagementProp) {
const [selectedYear, setSelectedYear] = useState(dayjs());
const [salesList, setSalesList] = useState<Sales[]>([]);
const { refetch } = useQuery(
[QUERY_KEY.API_CUSTOMER_SALES, customerId, selectedYear.year()],
() => profitAPI.getSalesOfCustomer(customerId, selectedYear.year()),
{
refetchOnWindowFocus: false,
onSuccess: (data) => {
setInitialSalesList(data.customerSalesList);
setSalesList(data.customerSalesList);
},
}
);
// ...
}
코드를 보면 딱히 이상한 점은 찾을 수 없었다.
그래서 React Query 깃헙 레포에다가 이슈를 하나 올렸다.
해당 이슈에 달린 댓글을 보면, 해결 방법을 가르쳐주신 분이 계셨다.
If you really want to sync state, prefer an effect…
즉, 상태를 동기화 하고 싶다면 useEffect
를 쓰라는 것이었다.
[실제 영상은 대외비라, 예시 영상은 제거하였습니다.]
오! 잘 된다.
그렇다면 무슨 차이가 있는 것일까?
이 또한 해당 스레드에 잘 나와 있는데, 다음과 같다.
Suspense
와 useQuery
, useEffect
가 동작하는 방식<Suspense>
컴포넌트가 마운트 된다.useQuery
)<Suspense>
컴포넌트가 자식의 비동기 함수를 관찰하고, 자식 컴포넌트를 언마운트한다. 그리고 폴백 컴포넌트를 마운트한다.useEffect
는 불리지 않는다. 왜냐하면 이미 언마운트 되었기 때문이다.data
), 해당 결과는 캐싱되며, <Suspense>
는 폴백 컴포넌트를 언마운트하고 자식 컴포넌트를 마운트한다.useEffect
가 호출되고, useQuery
는 캐시에 저장되어 있던 data
를 가져온다. 따라서 유저는 매끄러운 화면을 볼 수 있다.Suspense
와 useQuery
, onSuccess
가 동작하는 방식<Suspense>
컴포넌트가 마운트 된다.useQuery
는 Proimse를 던진다.<Suspense>
컴포넌트가 자식 컴포넌트를 언마운트한다. 그리고 폴백 컴포넌트를 마운트한다.data
), 해당 결과는 캐싱되며, onSuccess
가 호출된다.GitHub - setState in onSuccess is not working first time with suspense
[동료분의 분노의 리뷰...]