인프런 커뮤니티 질문&답변

나무아르볼님의 프로필 이미지

작성한 질문수

Next + React Query로 SNS 서비스 만들기

react-intersection-observer로 더 불러오기

react-intersection-observer 무한스크롤페이지 중복호출문제

작성

·

874

0

안녕하세요 제로초님 강의 잘 보고 있습니다.
다름아니라 무한스크롤 부분 구현중(개인 프로젝트에 해당 부분만 먼저 적용중이라 jsonplaceholder API로 호출하고 있습니다) fetchNextPage가 2번씩호출이 되어서 delay도 조정해보았는데 계속 2번씩호출이 되서 로직에 혹시 문제가 있는지 봐주실수 있을지부탁드립니다.
0. 콘솔에 확인한 결과
처음 화면에 ref가 렌더되고 그 이후 data fetching이 완료되어 isFetcing이 false가 되었는데, data를 map으로 div로 뿌리는 렌더링 과정에 시간이 소모되어 inView는 true && isFetching도 false가 되어서 div가 화면에 나타나고 ref div를 가리기 전에 data fetching을 한번씩 더 하고 있는 상황입니다.
감사합니다

답변 2

1

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

저도 사실 처음에는 두 번 호출되기는 하는데요. 스크롤 내렸을 때도 두 번씩 더 호출하나요?

getPosts를 throttling 처리하면 되긴 합니다.(react-query throttle로 검색)

저는 계속 2번씩 호출이 되는데 제가 개인프로젝트에 적용하는 과정에서 코드가 달라진 부분이 있어서 그런것같습니다. 네 알려주신대로 한번 적용해보겠습니다 감사합니다.

0

혹시 throttle 해결하셨을까요?? 저도 동일한 문제인데 도저히 감이 안와서 댓글 남깁니다 ㅠㅠㅠ

일단 아래와 같이 해결해서 중복호출 막았습니다.

혹시나 보시게되면 throttle 어떻게 하셨는지 보여주실 수 있으실까요??

  useEffect(() => {
    if (inView) {
      // 화면에 보일 때
      !isFetching && hasNextPage && fetchNextPage();
    }
  }, [inView, hasNextPage, fetchNextPage]);

 

const throttledFetchNextPage = throttle(() => {

!isFetching && hasNextPage && fetchNextPage()

}, 3000)

useEffect(() => {

if (inView && hasNextPage && !isFetching) {

throttledFetchNextPage()

}

}, [inView, hasNextPage, isFetching, throttledFetchNextPage])


저는 일단 lodash설치하고 거기서 throttle import해서 사용했습니다
구글링해보니 throttle같은 간단한 기능은 라이브러리 사용하는것보다 직접 utils에 구현해놓고 import해서 쓰는게 좋다고 하더라구요,

좋은 코드가 아닌것같은데 그래도 일단 공유드립니다

 

요롷게도 해결했습니다
마지막 아이템한테만 ref를 넘겨주어 감싸는 태그 ref넣어주기!

useEffect(() => {
    if (inView) {
      hasNextPage && fetchNextPage();
    }
  }, [inView, hasNextPage, fetchNextPage]);

  return res?.pages?.map((pages, pageIndex) => (
    <Fragment key={pageIndex}>
      {pages.map((e, itemIndex) => {
        const lastPage = pageIndex === res.pages.length - 1;
        const lastItemInPage = itemIndex === pages.length - 1;
        const isLastItem = lastPage && lastItemInPage;
        if (isLastItem) {
          return <MainCenterListItem key={e.id} {...e} ref={ref} />;
        }
        return <MainCenterListItem key={e.id} {...e} />;
      })}
    </Fragment>
  ));

혹시 비슷한 고민을 하시는 분들이 계실까 해서 늦었지만 남겨봅니다.

일단 처음 확인해볼것이
1. ref하나에서 fetch가 두번되느냐
2. ref가 (새로운 데이터가 fetching은 끝났으나 render되지 않아서) 화면에 여러번 나오는가
이것을 확인하기 위해 아래 첨부한 코드 맨 밑의 getRandomColor함수로 ref배경색을 계속 바꿔줬습니다.

1. ref하나에서 fetch가 두번 된다면 IntersectionObserver를 사용하는 로직에 문제가 있다는 것이고 (아닐 수도 있음)
저는 2번 경우였고 아래 코드중 위에(첫번째 return문으로 해결했습니다.)
jsx문법의 특성상 조건이 있는 블럭 &&나 || 등 조건이 있는 블럭을 만나면
그 블럭이 완전히 실행 된 후
그 밑의 코드가 실행된다고 합니다.
이렇게 해서 data render가 완전히 실행 된 후 div가 나타나게 하는 식으로 결국 해결했습니다.
{condition

 return (
    <VStack spacing={8} align='stretch'>
      {posts?.pages &&
        posts.pages.map((page, i) => (
          <Box key={i}>
            {page.map((post) => (
              <FeedPost key={post.id} post={post} />
            ))}
          </Box>
        ))}
      {hasNextPage && (
        <Box
          ref={intersectRef}
          h={'100px'}
          backgroundColor={getRandomColor()}
        />
      )}
      {posts?.pages.length === 0 && (
        <Text fontSize={'md'} color={'blue.400'}>
          다른 유저를 팔로우하고, 게시글을 확인해보세요 :D
        </Text>
      )}
    </VStack>
  );
 return (
    posts?.pages && (
      <Container maxW={'container.sm'} py={10} px={2}>
        {posts.pages.map((page, i) => (
          <Box key={i}>
            {page.map((post) => (
              <FeedPost key={post.id} post={post} />
            ))}
          </Box>
        ))}
        {hasNextPage && (
          <Box
            ref={intersectRef}
            h={'1000px'}
            backgroundColor={getRandomColor()}
          />
        )}
        {posts?.pages.length === 0 && (
          <Text fontSize={'md'} color={'blue.400'}>
            다른 유저를 팔로우하고, 게시글을 확인해보세요 :D
          </Text>
        )}
      </Container>
    )
  );
function getRandomColor() {
  // 랜덤한 색상 생성
  const letters = '0123456789ABCDEF';
  let color = '#';
  for (let i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
}