TanStack

TanStack(react) Query는 서버에서 가져오는 데이터를 효율적으로 가져올 수 있다!

사용하기 전에 QueryClientProvider 로 최상단에서 감싸준 뒤 사용하면 된다.

1. useQuery

밑이 useQuery의 기능과 옵션인데 너무 길다.

const {
  data,
  dataUpdatedAt,
  error,
  errorUpdatedAt,
  failureCount,
  failureReason,
  fetchStatus,
  isError,
  isFetched,
  isFetchedAfterMount,
  isFetching,
  isInitialLoading,
  isLoading,
  isLoadingError,
  isPaused,
  isPending,
  isPlaceholderData,
  isRefetchError,
  isRefetching,
  isStale,
  isSuccess,
  promise,
  refetch,
  status,
} = useQuery(
  {
    queryKey, //필수
    queryFn, //필수
    gcTime, // 여기부터 옵션
    enabled,
    networkMode,
    initialData,
    initialDataUpdatedAt,
    meta,
    notifyOnChangeProps,
    placeholderData,
    queryKeyHashFn,
    refetchInterval,
    refetchIntervalInBackground,
    refetchOnMount,
    refetchOnReconnect,
    refetchOnWindowFocus,
    retry,
    retryOnMount,
    retryDelay,
    select,
    staleTime,
    structuralSharing,
    subscribed,
    throwOnError,
  },
  queryClient
);

따라서 우선 자주 쓰는 것만 알아보도록 하자!

const { data, isLoading, error } = useQuery(queryKey, queryFn, ...option);
  1. queryKey queryKey: unknown[]
    쿼리 캐싱을 관리하도록 도와주는 역할을 한다. 이 값이 변경되면 쿼리가 자동으로 업데이트 한다! 즉 값이 변경되지 않으면 업데이트를 안한다.
  2. queryFn
    (context: QueryFunctionContext) => Promise<TData>
    쿼리에서 데이터를 요청할 때 사용하는 함수. axios에서 api로 정보를 받아오는 함수를 보통 쓴다.

그 다음은 return 값들이다.

  1. status: 만약 데이터를 받아 오지 않았거나 받아오는 와중의 상태를 나타낸다. Pending, error, success 등이 있고 관련해서 boolean으로 표현하는 건 isPending, isSuccess, isError, isLoadingError등 다양하게 있다!
  2. data: 받아온 데이터. 기본은 undefined이다.

2. useQueries

자 복수형이다. 그렇다는 말은? 여러 번 실행한다는 말이다.

따라서 useQueries는 useQuery의 훅과 동일한 쿼리 옵션 객체가 있는 배열 형태를 갖추고 있다.

const ids = [1, 2, 3];
const results = useQueries({
  queries: ids.map((id) => ({
    queryKey: ["post", id],
    queryFn: () => fetchPost(id),
    staleTime: Infinity,
  })),
});

3. useInfiniteQuery

말그대로 무한 쿼리. 훅은 다 비슷하지만…

pageParam과 getNextPageParma, getPreviousPageParam이 추가 됐다.

const {
  fetchNextPage,
  fetchPreviousPage,
  hasNextPage,
  hasPreviousPage,
  isFetchingNextPage,
  isFetchingPreviousPage,
  promise,
  ...result
} = useInfiniteQuery({
  queryKey,
  queryFn: ({ pageParam }) => fetchPage(pageParam),
  initialPageParam: 1,
  ...options,
  getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
    lastPage.nextCursor,
  getPreviousPageParam: (firstPage, allPages, firstPageParam, allPageParams) =>
    firstPage.prevCursor,
});
  1. initalPageParam: 기본으로 설정할 페이지 파라미터. 즉 페이지의 파라미터를 몇부터 시작할 것인가를 설정할 수 있다.
  2. getNextPageParam: 다음 페이지를 뭘 가져올 지 결정해주는 함수!. 옵션이다. 매개변수는 lastPage, allPage를 받는다. lastPage는 마지막으로 가져온 페이지의 데이터, allPages는 이전에 가져온 모든 페이지(누적)가 포함된 배열이다.

활용 방법은 매개변수인 allpages를 활용해서 다음 페이지를 뭐로 할 지 가져오고 마지막 페이지라면 실행시키지 않게 끔 한다.

```tsx
getNextPageParam: (lastPage, pages): number | false => {
            const nextPage = pages.length + 1;
            return lastPage.length === 0 ? false : nextPage;
        },
```
  1. getPreviousPageParam: NextPage와 반대로 이전 페이지로 갈때 어떻게 할지 정해주는 옵션이라고 생각하면 된다.

Return

  1. data.pages:TData[] 모든 페이지를 포함한 배열
  2. data.pagesParams: unknown[], 페이지의 파라미터를 가진 배
  3. hasNextPage: 다음 페이지가 있는지 여부를 나타내는 boolean 값이다.
  4. fetchNextPage는 다음 페이지의 데이터를 가져올 때 사용한다. 이전 호출 해결 여부에 상관없이 매번 qeuryFn을 호출한다. 여기서 cancelRefetch를 설정하며 해결한 뒤에 불러오도록 한다. 기본은 true!

Intersection Observer

Intersection observer는 타겟 요소와 상위 요소 또는 최상위 document 의 viewport 사이의 intersection 내의 변화를 비동기적으로 관찰하는 방법이다. 즉 타켓 대상이 보여지는 화면에 있는지 여부를 확인! 이걸 무한 스크롤과 접목 시키면, 된다 ㅎㅎ

  1. ref 생성 (타켓 요소 지정): 무한 스크롤의 트리거가 될 요소를 가리키게 한다. 즉 스크롤이 이 요소에 도달하면 이벤트가 발생하도록

    const ref = useRef<HTMLDivElement>(null);
    
  2. intersectinObserver 콜백함수 생성: 이 observer가 감지하는 이벤트가 발생했다면 실행되게한다. entry.isIntersecting은 요소가 화면에 보이는지 체크 hasNextPage는 다음 페이지가 있을때만 fetchNextPage를 실행하도록 하기 useCallback 사용해서 불필요하게 재생하지 않도록 방지.

    const handleIntersect: IntersectionObserverCallback = useCallback(
    ([entry]: IntersectionObserverEntry[], observer: IntersectionObserver) => {
    if (entry?.isIntersecting && hasNextPage) {
    fetchNextPage();
    }
    },
    [fetchNextPage, hasNextPage]
    );
    
        ```
    
    
  3. intersection Observer 설정하기. useEffect로 컴포넌트가 마운트되면 IntersectionObserver를 설정한다. observer.observe(ref.current)ref 가 가리키는 요소를 감시하기 시작하고 threshold: 0.6로 요소가 60퍼 이상 보일때 감지하도록 설정한다. return () ⇒observer.disconnect(); 로 컴포넌트가 언마운트되면 감시를 끝내도록 한다!

    useEffect(() => {
    let observer: IntersectionObserver;
    if (ref.current) {
    observer = new IntersectionObserver(handleIntersect, { threshold: 0.6 });
    observer.observe(ref.current);
    }
    return () => observer && observer.disconnect();
    }, [ref, handleIntersect]);
    
        ```
    
    
  4. ref 반환하기

function useInfiniteScroll({
  hasNextPage,
  fetchNextPage,
}: IntersectionObserverProps) {
  const ref = useRef<HTMLDivElement>(null);

  const handleIntersect: IntersectionObserverCallback = useCallback(
    ([entry]: IntersectionObserverEntry[], observer: IntersectionObserver) => {
      if (entry?.isIntersecting && hasNextPage) {
        fetchNextPage();
      }
    },
    [fetchNextPage, hasNextPage]
  );

  useEffect(() => {
    let observer: IntersectionObserver;
    if (ref.current) {
      observer = new IntersectionObserver(handleIntersect, { threshold: 0.6 });
      observer.observe(ref.current);
    }
    return () => observer && observer.disconnect();
  }, [ref, handleIntersect]);

  return ref;
}