two children with the same key 에러(feat : react-query, useInfiniteQuery, firebase)

bebrain·2023년 4월 1일
0

Infinite scroll을 적용하고 전체화면으로만 보다가 화면사이즈를 줄여봤더니 이런 에러가 콘솔창에 떴다.

희한한 것은 화면사이즈별로 에러유무가 달라졌다.

이렇게 크게 볼 때는 에러가 없는데

화면을 줄이면 바로 에러가 발생했다.

(같은 데이터를 중복해서 불러오고 있음)


이유를 곰곰히 생각해봤다.

hasNextPage가 true(다음에 불러올 데이터가 있는지 유무)이면 fetchNextPage()를 실행시켜 데이터를 가져오는 로직이다.

하지만 문제는 기본으로 6개씩(first) 가져오게 설정해 놓았지만, 화면을 줄였을 때는 이미지가 줄어 들고(가변그리드로 설정했다) 그에 따라 화면의 기본 길이를 채우기 위해 데이터가 첫 렌더링시 6개보다 더 불러와지게 된다.

하지만 이 때 스크롤을 아래로 움직이면 react-infinite-scoller가 감지하고 hasMore와 loadMore를 실행시킨다.

<InfiniteScroll
   loadMore={() => fetchNextPage?.()}
   hasMore={hasNextPage}
   loader={<div key={0}>Loading ...</div>}
                    className="w-full grid mx-auto sm:mx-0 sm:grid-cols-2 lg:grid-cols-3 gap-x-7 gap-y-9 relative pb-24"
>
       {totalItems.map((item) => {
                        return <RecipeListData key={item.id} item={item} />;
       })}
</InfiniteScroll>

hasNextPage는 다음에 가져올 데이터가 있으니 당연히 true이고 그에 따라 fetchNextPage()가 실행되어 기본 6개 데이터(first)의 다음 순서부터 차례대로 불러온다.

결과적으로 렌더링시 이미 불러온 6개 이상의 데이터([1, 2, ..., 6, 7, 8, 9, 10]) + fetch로 가져온 first 바로다음 6개의 데이터([7, 8, 9, 10, 11, 12])에 중복이 발생하는 것이다.

그래서 원래 6개씩 가져오던 로직을 12개로 바꾸었더니 에러가 사라졌다.

// 전체목록(12개씩)
const first = async () => {
        const querySnapshot = await getDocs(
            query(
                collection(dbService, "recipe"),
                orderBy(isBest ? "viewCount" : "createdAt", "desc"),
                limit(12)
            )
        );
        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" : "createdAt", "desc"),
                startAfter(pageParam),
                limit(12)
            )
        );
        const newData = querySnapshot.docs.map((doc: any) => ({
            ...doc.data(),
            id: doc.id,
        }));
        setTotalItems((prev) => [...prev, ...newData]);
        return querySnapshot;
};

// InfiniteQuery
const { isLoading, isError, error, fetchNextPage, hasNextPage } =
        useInfiniteQuery<any, Error>(
            ["infiniteRecipe", isBest],
            async ({ pageParam }) =>
                await (pageParam ? next(pageParam) : first()),
            {
                getNextPageParam: (querySnapshot) => {
                    const lastPageParam =
                        querySnapshot.docs[querySnapshot.docs.length - 1];
                    return querySnapshot.size < 12 ? undefined : lastPageParam;
                },
                refetchOnWindowFocus: false,
            }
);

0개의 댓글