아래와 같이 useInfiniteQuery가 반환하는 hasNextPage boolean 값으로 observer
를 조건부 렌더링 했습니다.
// 원래 방식
{ loadData &&
<S.ObserveDiv ref={setObservationTarget}></S.ObserveDiv>
}
// 바꾼 방식
{ hasNextPage &&
<S.ObserveDiv ref={setObservationTarget}></S.ObserveDiv>
}
그리고 다음 페이지를 불러와서 portfolios가 바뀔 경우 useEffect를 발동시켜 showedPortfolioNum 을 조작해 화면에 보여주는 아이템 수를 업데이트 했습니다. showedPortfolioNum 라는 변수 네이밍이 너무 길고 별로라서 간단하게 showsNum 라고 바꿨습니다.
Bug Report
개요
PortfolioList.tsx에서 무한스크롤을 구현하는 중에 발생한 문제입니다.
useIntersectionObserver 커스텀훅을 만들어서 초기값으로
그런데 callback 안에 부모 컴포넌트에서 관리하는 state를 다루는 로직이 포함됩니다.
부모 컴포넌트에서는 상태값이 변하지만, 초기값으로 전달된 callback 함수는 전달된 그 순간에서부터 내부 변수가 더이상 변하지 않습니다. 전달 받은 함수를 그대로 유지하고 다시 새롭게 생성되지 않기 때문입니다.
📸 Screenshots
https://github.com/Kim-DaHam/Portfolly/assets/81691456/32fdfeaa-f22e-4dcb-b00f-8c5fe5d5f0bf
💻 Code
부모 컴포넌트에서 관리하는 showedPortfolioNum라는 state가 있습니다.
서버에서 데이터를 100개정도 받아온 다음 30개씩 끊어 보여주는 방식으로 무한스크롤을 만들 계획인데(한 페이지=100개, 한 파트=30개), showedPortfolioNum가 지금까지 보여준 데이터 개수를 의미합니다.
그래서 observer가 발동했을 때 바로 데이터 패칭을 하는 게 아닌, showedPortfolioNum 상태값을 조정해야 합니다.
🙁 Actual behavior
useIntersectionObserver 초기 props로 상태값을 다루는 callback을 넘겨줬을 때, 상태가 업데이트 되지 않는다.
🙂 Expected behavior
useIntersectionObserver 훅을 재사용 가능하게 최대한 더럽히지 않고, showedPortfolioNum state를 조작하고 싶습니다.
추가 사항
-
해결 방법
new IntersectionObserver
객체에 들어가는 콜백 함수는 단순 동작만 하도록 만들었습니다.그리고
useSuspenseInfiniteQuery
의getNextPageParam
이 다음 조건에 따라 다음 페이지를 불러올 건지 말 건지 결정하기 때문에,아래 조건문은 중복 검사가 되기 때문에 제거하고,
아래와 같이
useInfiniteQuery
가 반환하는hasNextPage
boolean 값으로 observer그리고 다음 페이지를 불러와서 portfolios가 바뀔 경우 useEffect를 발동시켜 showedPortfolioNum 을 조작해 화면에 보여주는 아이템 수를 업데이트 했습니다.
showedPortfolioNum
라는 변수 네이밍이 너무 길고 별로라서 간단하게showsNum
라고 바꿨습니다.그리고 반환되는 데이터는 pages: Array[] 형태이기 때문에, pages를 flat() 메서드로 1차원 배열로 풀어줬습니다.
아래 링크에선 flat() 메서드 보다 concat으로 잘라 붙이는 게 속도가 더 빠르다고 하는데.. 코드가 간단해 보이길 원하는데다 데이터 수가 그렇게 많지 않기 때문에 그냥 flat을 사용합니다.
중첩 배열을 1차원 배열로 푸는 방법
기타 궁금한 점
new IntersectionObserver
에setIsObserve(true)
를 넣어useEffect(void, [isObserve])
조건부 실행을 시킨 다음 그 안에서showedPortfolioNum
를 조작하려고 했습니다.그런데 초기 렌더링 2번 +
new IntersectionObserver
객체가 생성된 직후 첫 콜백 실행을 제외하고는 setIsObserve(true)에 따라 useEffect가 발동하지 않았습니다.그 원인은 추후 자세하게 알아봐야합니다.