useInfiniteQuery로 무한스크롤을 구현하며 사용하고 있었다.
const Component = () => {
const {ref, inView} = useInView();
const {data, fetchNextPage} = useGetData();
useEffect(() => {
if (inView) {
fetchNextPage();
}
}, [inView]);
}
이게 게시판이라고 치면 게시글들을 useGetData를 통해 가져오고 해당 목록을 클릭하면 상세페이지로 이동하여 해당 게시물의 조회수가 갱신되는 로직이다.
그런데 잘 작동하는 듯 했지만 게시글의 목록이 fresh한 상태로 조회되지 않는 것을 발견하였다.
export const useGetData = (option) => {
const { data, fetchNextPage } = useSuspenseInfiniteQuery({
queryKey: ["getData", option],
queryFn: async ({ pageParam = 1 }) => {
const res = await getDataApi({ ...option, pageNo: pageParam });
return res?.data?.result ?? [];
},
getNextPageParam: (lastPage, allPages) => {
return lastPage.length > 0 ? allPages.length + 1 : undefined;
},
});
return { data: data?.pages.flat() ?? [], fetchNextPage };
};
문제가 됐던 해당 useInfiniteQuery hook이다.
여기서 분명 상세페이지로 이동했다가 다시 돌아오면 분명히 queryFn이 작동하면서 api 통신을 했다.
하지만 return 된 값은 호출하여 받아온 신선한 상태의 데이터가 아닌 캐시에 저장된 old data를 계속 return하고 있었다.
이에 나는 hook을 사용하는 곳에서 useEffect를 통해 해당 쿼리 키를 invalidate, remove, reset 별의 별 무효화를 다 시켰지만 효과 있었던 것은 cancelQueries 하나였다. 그런데 cancelQueries를 써주면 원치않았던 api 호출이 굉장히 많아졌고 좋지 않다고 생각했다.
react query 공식 github의 토론 페이지나 issue 페이지 및 stackoverflow를 열심히 조사한 끝에 나와 비슷한 문제를 겪고 있던 사람의 게시물을 봤고 해결책 또한 알게 되었다.
const Component = () => {
const {ref, inView} = useInView();
const {data, fetchNextPage} = useGetData();
useEffect(() => {
if (inView && data?.length >= offset) {
// 한 화면에 보이는 데이터의 최대 개수 즉 스크롤이 하단에 닿을때 다음페이지를 가져오도록 한다.
fetchNextPage();
}
}, [inView]);
}
아마 정확하진 않지만 useInfiniteQuery 에서 동작을 할때 두개의 페이지가 모두 있는 상태에서 페이지에 재진입하게 되면 첫번째 페이지가 제대로 갱신되지 않은 문제였던 것 같다.
return 된 data를 console로 살펴보면 pageParams의 0번째 값이 null이던데 이것이 문제일수도 있고..
해결 방법은 단순했다.
scroll의 기능이 구동할 때 즉, api에서 받은 데이터의 개수가 보여지는 데이터 개수와 동일할때 다음 페이지를 가져오는 fetchNextPage를 호출해주면 된다.
유명하고 좋은 라이브러리지만 무조건 믿고 쓰는건 안된다는 것을 다시 한번 배웠다.
아마 버그가 아닐까 싶다.. react qeury 공식문서에서 useInfiniteQuery를 쳐봤는데 바로 몇주전까지 토론을 이어가고 있었다. 휴..
댓글