본문 바로가기

기록/TIL

2023.09.25 프로젝트 시작부터 괴롭히던 무한스크롤 응급처치

드디어 무한 스크롤이 두 번씩 발동되던 버그를 고쳤다.
고쳤다고 하기엔 아직 문제가 있어, 사실 응급처치라고 해도 될 것 같다.
 
결론부터 말하면, 
기존 무한스크롤에서는, useQuery의 isLoading과 isFetchig이 끝났을 때 ref를 생성하여 무한스크롤을 동작시키고 있었다.
여기서 문제가 있었던게 isLoading과 isFetching은 끝났어도 요소가 아직 생성되지 않은 상태에서 ref가 생성되니, ref를 또 참조하게 되어 두 번씩 작동하던것. 
 
처음에는, throttle을 주는 시점이 잘못된 줄 알고 offset을 증가하는 함수에서 Intersection Observer로 ref를 감지하는 함수로 throttle을 옮겨도 보고, 직접 throttle을 만들어도보고, 시간 조절도 해 봤으나 고쳐지지 않았다.
 
throttle 함수

const throttledLoadMore = throttle(() => {
    if (isNextPage && !isFetching && !isLoading) {
      // 이전 offset에 size를 더하여 다음 페이지 데이터를 가져오도록 설정
      setOffset((prevOffset) => prevOffset! + size);
    }
  }, 1000);

 
두 번째로는 ref가 계속 생성되어 throttle이 먹지 않는가 싶어 ref를 조건부 렌더링이 아닌 위 코드처럼 함수 자체에 조건을 줘보았으나, 이 또한 아니었다.
 
몇일이 지난 오늘, 마지막으로 state를 사용하여 loading 완료 여부를 직접 조작 하였더니 드디어 한 번에 한 번만 작동하게 되었다!! 
 
기존 코드

const animeListQueryOptions = {
    queryKey: animeListQueryKey,
    queryFn: () => fetchAnimeList(params);
    refetchOnWindowFocus: false,
    onSuccess: (data: any) => {
      setIsNextPage(data.isNextPage);
      setCount(data.count);
      setAnimeList((prevAnimeList) => [...prevAnimeList, ...data.animeList]);
    },
  };
   
  // query로 데이터를 받아와 set해준다.
  ....
  
 {!isLoading && isNextPage && <S.Target ref={ref} />}
 // loading이 끝나고 nextpage가 있다면 ref를 생성한다.

 
 
수정 된 코드

const [isLoaded, setIsLoaded] = useState(false);

const animeListQueryOptions = {
    queryKey: animeListQueryKey,
    queryFn: async () => {
      setIsLoaded(false);
      const data = await fetchAnimeList(params);
      return data;
    },
    refetchOnWindowFocus: false,
    onSuccess: (data: any) => {
      setIsNextPage(data.isNextPage);
      setCount(data.count);
      setAnimeList((prevAnimeList) => [...prevAnimeList, ...data.animeList]);
      setIsLoaded(true);
    },
  };
  
  // isLoaded란 state를 추가.
  // queryFn을 실행할 때 false를 주고, onSuccess를 통해 true를 준다.
  
  ......
  
<S.Target ref={ref} />
// ref는 항상 생성되어 있고,

const throttledLoadMore = throttle(() => {
    if (isNextPage && !isFetching && !isLoading && isLoaded) {
      // 이전 offset에 size를 더하여 다음 페이지 데이터를 가져오도록 설정
      setOffset((prevOffset) => prevOffset! + size);
    }
  }, 1000);
  
// 함수에 조건을 붙여 로딩이 모두 끝나야만 offset을 증가시킨다.

'기록 > TIL' 카테고리의 다른 글

HTTPS :  (0) 2023.10.18
DDD : Domain Driven Design  (0) 2023.10.17
2023.09.20  (0) 2023.09.20
2023.09.15 useMemo로 최적화 해보기  (0) 2023.09.15
2023.09.13  (0) 2023.09.13