250x250
반응형
arkhyeon
arkhyeon
arkhyeon
전체 방문자
오늘
어제
  • 분류 전체보기 (88)
    • Spring (5)
    • Java (4)
    • React (25)
      • TypeScript (6)
      • JavaScript (1)
      • Jest (9)
    • NEXT (8)
    • SQL (1)
    • React native (1)
    • CSS (3)
    • Web (1)
    • Git (3)
    • ETC (6)
    • 빅데이터DB (8)
    • Docker (4)
    • Tool (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • react loading
  • usetransition
  • websocket server
  • docker tomcat
  • react usetransition
  • react jest
  • HIVE
  • react
  • WSS
  • javascript wss
  • react19
  • javasciprt websocket
  • Spring WebSocket
  • kudu
  • websocket
  • jest
  • react spring websocket
  • react websocket
  • react typescript
  • node WebSocket

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
arkhyeon

arkhyeon

React/TypeScript

React TypeScript useInfiniteQuery Infinite Scroll

2024. 3. 15. 10:11
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

  1. 감시되었는지 아닌지에 대한 분기 처리
  2. 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
    'React/TypeScript' 카테고리의 다른 글
    • React Typescript Interface Tuple Generic
    • React RichTextEditor Image Upload - reactQuill
    • React TypeScript Zustand Infinite Scroll with IntersectionObserver
    • React Axios TypeScript with JWT Boilerplate
    arkhyeon
    arkhyeon

    티스토리툴바