TIL 73. Next.js - firebase + useInfiniteQuery

isk·2023년 2월 14일
0

TIL

목록 보기
70/122
post-custom-banner

useInfiniteQuery부분은 전 게시물에서 다룬 내용이다.
useInfiniteQuery와 firebase를 가지고 무한 스크롤을 구현할 것이기 때문에 다시 기재한다.

useInfiniteQuery

 const {
   data, 					// data.pages를 갖고 있는 배열
   error, 					// error 객체
   fetchNextPage, 			// 다음 페이지를 불러오는 함수
   fetchPreviousPage,		// 이전 페이지를 불러오는 함수
   hasNextPage, 			// 다음 페이지가 있는지 여부, Boolean
   hasPreviousPage,			// 이전 페이지 여부, Boolean
   isFetchingNextPage,		// 추가 페이지 fetching 여부, Boolean
   isFetchingPreviousPage,	// 이전 페이지 fetching 여부, Boolean
   status, 					// loading, error, success 중 하나의 상태, string
   ...result
 } = useInfiniteQuery(queryKey, ({ pageParam = 1 }) => fetchPage(pageParam), {
   ...options,
   getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
   getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
 })

위 코드는 공식문서에 나와있는 코드에 내 입맛을 추가한 코드다.

공식문서의 설명은 살짝 불친절한 감이 있어서 실제 코드를 보면서 알아보자.

const {
    data, 
    fetchNextPage, 
    status, 
  } = useInfiniteQuery(
    'infiniteData', 
    getData,
    {
      getNextPageParam: () => {
        return true;
      },
    }
  );

내가 가져올 데이터는 페이지 파라미터가 없다. 그래서 리턴값으로 true를 주었다.
물론 getNextPageParam부분이 없으면 다음페이지를 가져오지 않는다.

useBottomScrollListener(() => {
    fetchNextPage();
  });

useBottomScrollListener라는 라이브러리를 사용해서 스크롤이 바닥에 닿을 때 fetchNextPage()함수를 실행시킨다.
fetchNextPage()함수는 useInfiniteQuery의 다음페이지를 불러오는 함수다.


firebase

데이터는 파이어베이스를 사용해서 가져오는데,
파이어베이스는 쿼리커서로 페이지를 나눠주는 기능을 아래처럼 제공한다.

import { collection, query, orderBy, startAfter, limit, getDocs } from "firebase/firestore";

// Query the first page of docs
const first = query(collection(db, "cities"), orderBy("population"), limit(25));
const documentSnapshots = await getDocs(first);

// Get the last visible document
const lastVisible = documentSnapshots.docs[documentSnapshots.docs.length-1];
console.log("last", lastVisible);

// Construct a new query starting at this document,
// get the next 25 cities.
const next = query(collection(db, "cities"),
    orderBy("population"),
    startAfter(lastVisible),
    limit(25));

위 코드가 쿼리커서로 페이지를 나누는 코드다.
startAt() 또는 startAfter()메서드를 사용하여 쿼리의 시작점을 정의한다.
startAt()메서드는 시작점을 포함하고, startAfter() 메서드는 시작점을 제외한다.
예를 들어 쿼리에 startAt(A)을 사용하면 전체 알파벳이 반환되고, startAfter(A)를 대신 사용하면. B-Z가 반환된다.

위 코드를 설명하면, 처음엔 25개의 데이터만 불러온다.
그 후로 startAfter(lastVisible)을 지정해주고, 다음부터 lastVisible를 넣은 25개의 데이터를 불러온다.
lastVisible를 넣었기 때문에 lastVisible에 지정된 docs를 제외한 이후의 25개의 데이터를 불러온다.

위 공식문서 코드를, 조건문을 사용해서 현제 프로젝트에 맞게 변형한 코드가 아래 코드다.

let lastVisible: any = undefined;
export const getData = async () => {
  const getData: any = [];
  let q;
  if (lastVisible === -1) {
    return;
  } else if (lastVisible) {
    q = query(
      collection(dbService, 'test'),
      orderBy('createdAt', 'desc'),
      limit(4),
      startAfter(lastVisible)
    );
  } else {
    q = query(
      collection(dbService, 'test'),
      orderBy('createdAt', 'desc'),
      limit(16)
    );
  }

  const querySnapshot = await getDocs(q);
  querySnapshot.forEach((doc) => {
    getData.push({ id: doc.id, ...doc.data() });
    if (querySnapshot.docs.length === 0) {
      lastVisible = -1;
    } else {
      lastVisible = querySnapshot.docs[querySnapshot.docs.length - 1];
    }
  });

  return getData;
};

더이상 불러올 데이터가 없는 경우 lastVisible를 -1로 정해주는 것으로, 함수가 실행되지 않게 할 수 있다.


개인적으로 타입에 any가 있으면 찝찝하기 때문에 이제 any를 없애러 가야겠다.

post-custom-banner

0개의 댓글