728x90
반응형
ReactQuery TanStack useInfiniteQuery
fetchNextPage | 다음 페이지 호출 |
fetchPreviousPage | 이전 페이지 호출 |
hasNextPage | 다음 페이지 존재 여부 |
hasPreviousPage | 이전 페이지 존재 여부 |
isFetchingNextPage | 다음 페이지 불러오는중 인지 |
isFetchingPreviousPage | 이전 페이지 불러오는중 인지 |
queryKey | 쿼리 고유 식별자(캐시, 상태관리) |
queryFn | 데이터 가져오는 함수로 페이지 파라미터를 인자 받음 |
initialPageParam | 초기 페이지 파라미터 설정 |
…options | 추가적인 옵션 전달 |
getNextPageParam | 다음 페이지 이동 시 페이지 파라미터를 반환 |
getPreviousPageParam | 이전 페이지 이동 시 페이지 파라미터를 반환 |
const {
fetchNextPage,
fetchPreviousPage,
hasNextPage,
hasPreviousPage,
isFetchingNextPage,
isFetchingPreviousPage,
...result
} = useInfiniteQuery({
queryKey,
queryFn: ({ pageParam }) => fetchPage(pageParam),
initialPageParam: 1,
...options,
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
lastPage.nextCursor,
getPreviousPageParam: (firstPage, allPages, firstPageParam, allPageParams) =>
firstPage.prevCursor,
})
Infinite Queries | TanStack Query Docs
tanstack.com
Ranking List Infinite Scroll Component
React TypeScript Zustand Infinite Scroll with IntersectionObserver
💡 기존 식상한 페이지 처리를 유튜브 댓글과 비슷하게 무한 스크롤로 구현하였고 이는 오직 Zustand 와 IntersectionObserver를 사용하였습니다. Zustand Setting 댓글 리스트를 관리할 개체들과 요청에 대
arkhyeon.tistory.com
ReactQuery(TanStack)에서는 useInfiniteQuery에서 필요한 것들을 모두 제공하여 이전 Zustand로 구현했던 것 보다 쉽게 구현이 가능합니다. Custom Hook을 사용했다는 것 이외에는 크게 달라지는 점은 없습니다.
function MemberRanking() {
//useInfiniteQuery 작성
const useFetchUsers = () => {
return useInfiniteQuery(
['ranking'],
({ pageParam = 0 }) => {
return getFeedPost(pageParam);
},
{
getNextPageParam: data => {
// 받은 데이터가 없으면 pageParam을 undefined로 반환
return data.userRankingDtos.length === 0 ? undefined : data.pageParam + 1;
},
},
);
};
// 작성된 useInfiniteQuery 사용을 위해 구조 분해
const { data, hasNextPage, isFetching, fetchNextPage, refetch } = useFetchUsers();
// useFetchUsers data 변경 시 마다 userRankingDtos 추가
const users = useMemo(
() => (data ? data.pages.flatMap(({ userRankingDtos }) => userRankingDtos) : []),
[data],
);
const getFeedPost = async (pageParam: number) => {
const res = await client.post<APIUserRankingType>(
`user/ranking`, { params: { page: pageParam, size: 20 } },
);
return { userRankingDtos: res.userRankingDtos, pageParam };
};
// 개체 감시 Custom hook
const ref = useIntersect(async (entry, observer) => {
observer.unobserve(entry.target);
if (hasNextPage && !isFetching) {
await fetchNextPage();
}
});
return (
<RankWrap>
{/* 유저 랭킹 검색 컴포넌트 */}
{users.map((ul, rank) => {
return (
<UserRank key={ul.userId}>
{/* 유저 랭킹 컴포넌트 */}
</UserRank>
);
})}
<div className="flex-cc">
{hasNextPage && !isFetching && <span className="loader"></span>}
</div>
<Target ref={ref} />
</RankWrap>
);
}
const Target = styled.div`
height: 1px;
`;
export default MemberRanking;
useIntersect Custom Hook
- 감시되었는지 아닌지에 대한 분기 처리
- Ref 활성화 후 감시 시작
무한 스크롤을 2개 이상 진행하면서 위와 같은 동일한 부분이 있어 Custom Hook으로 만들어 감시된 이후의 로직은 외부에서 사용할 수 있도록 함수로 받고 ref를 반환하도록 했습니다.
type IntersectHandler = (entry: IntersectionObserverEntry, observer: IntersectionObserver) => void;
export const useIntersect = (onIntersect: IntersectHandler, options?: IntersectionObserverInit) => {
// 감시당할 객체 생성
const ref = useRef<HTMLDivElement>(null);
// 감시되면 실행할 콜백 함수
const callback = useCallback(
(entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {
entries.forEach(entry => {
if (entry.isIntersecting) onIntersect(entry, observer);
});
},
[onIntersect],
);
useEffect(() => {
// ref 활성화 시 감시 시작
if (!ref.current) return;
const observer = new IntersectionObserver(callback, options);
observer.observe(ref.current);
return () => observer.disconnect();
}, [ref, options, callback]);
return ref;
};
728x90
반응형
'React > TypeScript' 카테고리의 다른 글
React Typescript Interface Tuple Generic (0) | 2024.03.25 |
---|---|
React RichTextEditor Image Upload - reactQuill (0) | 2024.03.18 |
React TypeScript Zustand Infinite Scroll with IntersectionObserver (0) | 2024.03.14 |
React Axios TypeScript with JWT Boilerplate (0) | 2024.03.14 |
React TypeSciprt - Community Project (0) | 2024.03.13 |