InfiniteScroll useInfinitequery(feat : firebase, react-infinite-scroller library)

bebrain·2023년 3월 23일
1
post-thumbnail

문제의 코드

const getList = async (pageParam: number) => {
        const querySnapshot = await getDocs(
            query(
                collection(dbService, "recipe"),
                orderBy(
                    isBest === "viewCount" ? "viewCount" : "createdAt",
                    "desc"
                ),
                startAfter(pageParam),
                limit(6)
            )
        );
        const newData = querySnapshot.docs.map((doc: any) => ({
            ...doc.data(),
            id: doc.id,
        }));
        setTotalItems((prev) => [...prev, ...newData]);
        return totalItems;
};

const { fetchNextPage, hasNextPage } = useInfiniteQuery(
        ["infiniteRecipe"],
        ({ pageParam = 0 }) => getList(pageParam),
        {
            getNextPageParam: (querySnapshot) => {
                const lastPageParam =
                    querySnapshot.docs[querySnapshot.docs.length - 1];
                if (querySnapshot.size < 6) {
                    return undefined;
                }
                return lastPageParam;
            },
        }
    );

startAfter(undefined)가 되어 유효하지 않은 조건이 되어 firebase 에러가 발생했다.
그래서 getList()를 first()와 next() 두 개의 함수로 분리해 보았다.

first는 초기 data 6개를 불러 오는 로직이고
next는 startAfter(firebase조건문)을 추가하여 기존data 바로 다음의 data 6개를 불러 오는 로직이다.

const first = async () => {
        const querySnapshot = await getDocs(
            query(
                collection(dbService, "recipe"),
                orderBy(
                    isBest === "viewCount" ? "viewCount" : "createdAt",
                    "desc"
                ),
                limit(6)
            )
        );
        const newData = querySnapshot.docs.map((doc: any) => ({
            ...doc.data(),
            id: doc.id,
        }));
        setTotalItems(newData);
};

const next = async (pageParam: number) => {
        const querySnapshot = await getDocs(
            query(
                collection(dbService, "recipe"),
                orderBy(
                    isBest === "viewCount" ? "viewCount" : "createdAt",
                    "desc"
                ),
                startAfter(pageParam),
                limit(6)
            )
        );
        const newData = querySnapshot.docs.map((doc: any) => ({
            ...doc.data(),
            id: doc.id,
        }));
        setTotalItems((prev) => [...prev, ...newData]);
};

const { fetchNextPage, hasNextPage } = useInfiniteQuery(
        ["infiniteRecipe"],
        async ({ pageParam }) => await (pageParam ? next(pageParam) : first()),
        {
            getNextPageParam: (querySnapshot) => {
                const lastPageParam =
                    querySnapshot?.docs[querySnapshot.docs.length - 1];
                if (querySnapshot?.size < 6) {
                    return undefined;
                }
                return lastPageParam;
            },
        }
);

getNextPageParam에서 first()와 next()의 querySnapshot을 받아오지 못했다.


최종 코드

// 전체목록(6개씩)
const first = async () => {
        const querySnapshot = await getDocs(
            query(
                collection(dbService, "recipe"),
                orderBy(
                    isBest === "viewCount" ? "viewCount" : "createdAt",
                    "desc"
                ),
                limit(6)
            )
        );
        const newData = querySnapshot.docs.map((doc: any) => ({
            ...doc.data(),
            id: doc.id,
        }));
        setTotalItems(newData);

        return querySnapshot;
    };
    
// 더보기event
const next = async (pageParam: number) => {
        const querySnapshot = await getDocs(
            query(
                collection(dbService, "recipe"),
                orderBy(
                    isBest === "viewCount" ? "viewCount" : "createdAt",
                    "desc"
                ),
                startAfter(pageParam),
                limit(6)
            )
        );
        const newData = querySnapshot.docs.map((doc: any) => ({
            ...doc.data(),
            id: doc.id,
        }));
        setTotalItems((prev) => [...prev, ...newData]);
        return querySnapshot;
};

//쿼리문
const { fetchNextPage, hasNextPage } = useInfiniteQuery(
        ["infiniteRecipe"],
        async ({ pageParam }) => await (pageParam ? next(pageParam) : first()),
        {
            getNextPageParam: (querySnapshot) => {
                const lastPageParam =
                    querySnapshot.docs[querySnapshot.docs.length - 1];
                if (querySnapshot.size < 6) {
                    return undefined;
                }
                return lastPageParam;
            },
        }
);
    
return (
        <InfiniteScroll
            loadMore={() => fetchNextPage()}
            hasMore={hasNextPage}
            loader={<div>불러오는 중...</div>}
        >
            {totalItems?.map((item) => {
                return (
                    <div key={item.id} className="aspect-[1/0.7]">
                    ...
        </InfiniteScroll>

first()와 next()에 각각

return querySnapshot;

으로 return문으로 처리하여 getNextPageParam이 querySnapshot을 인자로 넘겨 받을 수 있도록 고쳐주었다.

6개씩 불러오도록 설정하였는데 querySnapshot.size < 6 (불러와야 할 data의 개수가 6개이하)일 때는 undefined를 반환하여 더 이상 스크롤이 작동하지 않고 그 외에는 (불러와야 할 data의 개수가 6개이상) 스크롤이 작동하여 data를 불러 온다.

참조1 : https://engschool.tistory.com/m/76
참조2 : https://gingerkang.tistory.com/123

0개의 댓글