From bac0228d21b7195065e9cddcc327ae33659c7bcc Mon Sep 17 00:00:00 2001 From: dujinkim Date: Sun, 1 Jun 2025 13:52:21 +0000 Subject: (대표님) 20250601까지 작업사항 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hooks/use-data-table.ts | 91 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 32 deletions(-) (limited to 'hooks/use-data-table.ts') diff --git a/hooks/use-data-table.ts b/hooks/use-data-table.ts index f33c2e8b..d0521a7e 100644 --- a/hooks/use-data-table.ts +++ b/hooks/use-data-table.ts @@ -1,4 +1,4 @@ -// hooks/use-data-table.ts (업데이트) +// hooks/use-data-table.ts (무한 렌더링 수정) "use client" import * as React from "react" @@ -36,6 +36,7 @@ import useSWRInfinite from "swr/infinite" import { getSortingStateParser } from "@/lib/parsers" import { useDebouncedCallback } from "@/hooks/use-debounced-callback" +import isEqual from "fast-deep-equal" // 무한 스크롤 임계값 (이 값 이상이면 무한 스크롤 모드) const INFINITE_SCROLL_THRESHOLD = 1_000_000 @@ -59,6 +60,7 @@ interface UseDataTableProps | "onGroupingChange" | "onExpandedChange" | "getExpandedRowModel" + | "data" >, Required, "pageCount">> { filterFields?: DataTableFilterField[] @@ -150,7 +152,10 @@ export function useDataTable({ ) // pageSize 기반 무한 스크롤 모드 자동 결정 - const isInfiniteMode = perPage >= INFINITE_SCROLL_THRESHOLD + // const isInfiniteMode = perPage >= INFINITE_SCROLL_THRESHOLD + + const isInfiniteMode = !!(infiniteScrollConfig && perPage >= INFINITE_SCROLL_THRESHOLD) + // -------- Sorting URL 동기화 -------- const [sorting, setSorting] = useQueryState( @@ -211,7 +216,7 @@ export function useDataTable({ return Math.min(50, maxSize) // 기본 50개씩 로드 }, [isInfiniteMode, perPage, infiniteScrollConfig?.maxPageSize]) - // SWR 키 생성 함수 + // SWR 키 생성 함수 - 안정화를 위해 useCallback 사용 const getKey = React.useCallback( (pageIndex: number, previousPageData: InfiniteScrollResponse | null) => { if (!isInfiniteMode || !infiniteScrollConfig) return null @@ -273,12 +278,25 @@ export function useDataTable({ return swrData.flatMap(page => page.data) }, [swrData, isInfiniteMode]) - // 무한 스크롤 메타 정보 + // 무한 스크롤 메타 정보 - 안정화를 위해 useCallback 사용 + const infiniteScrollActions = React.useMemo(() => ({ + loadMore: () => { + if (swrData && swrData[swrData.length - 1]?.hasNextPage && !swrIsValidating) { + swrSetSize(prev => prev + 1) + } + }, + reset: () => { + swrSetSize(1) + swrMutate() + }, + refresh: () => swrMutate(), + }), [swrData, swrIsValidating, swrSetSize, swrMutate]) + const infiniteMeta = React.useMemo(() => { - if (!isInfiniteMode || !infiniteScrollConfig || !swrData) return null + if (!isInfiniteMode || !infiniteScrollConfig) return null - const totalCount = swrData[0]?.total ?? null - const hasNextPage = swrData[swrData.length - 1]?.hasNextPage ?? false + const totalCount = swrData?.[0]?.total ?? null + const hasNextPage = swrData?.[swrData.length - 1]?.hasNextPage ?? false const isLoadingMore = swrIsValidating && swrData && typeof swrData[swrSize - 1] !== "undefined" return { @@ -286,28 +304,35 @@ export function useDataTable({ totalCount, hasNextPage, isLoadingMore, - loadMore: () => { - if (hasNextPage && !isLoadingMore) { - swrSetSize(prev => prev + 1) - } - }, - reset: () => { - swrSetSize(1) - swrMutate() - }, - refresh: () => swrMutate(), + onLoadMore: infiniteScrollActions.loadMore, + reset: infiniteScrollActions.reset, + refresh: infiniteScrollActions.refresh, error: swrError, isLoading: swrIsLoading, isEmpty: swrData?.[0]?.data.length === 0, } - }, [isInfiniteMode, infiniteScrollConfig, swrData, swrIsValidating, swrSize, swrError, swrIsLoading, swrSetSize, swrMutate]) + }, [ + isInfiniteMode, + infiniteScrollConfig, + swrData, + swrIsValidating, + swrSize, + swrError, + swrIsLoading, + infiniteScrollActions + ]) + + // 검색어나 필터 변경 시 무한 스크롤 리셋 - infiniteMeta 의존성 제거 + const resetInfiniteScroll = React.useCallback(() => { + if (isInfiniteMode && infiniteScrollActions) { + infiniteScrollActions.reset() + } + }, [isInfiniteMode, infiniteScrollActions]) - // 검색어나 필터 변경 시 무한 스크롤 리셋 + // 필터 변경 시 리셋 - useEffect dependency 최소화 React.useEffect(() => { - if (isInfiniteMode && infiniteMeta) { - infiniteMeta.reset() - } - }, [search, parsedFilters, joinOperator, sortForSWR, isInfiniteMode]) + resetInfiniteScroll() + }, [search, filters, joinOperator]) // 최종 데이터 결정 const finalData = isInfiniteMode ? infiniteData : initialData @@ -325,11 +350,11 @@ export function useDataTable({ void setPage(1) // 무한 스크롤에서 페이지네이션으로 전환 시 무한 스크롤 데이터 리셋 - if (wasInfiniteMode && infiniteMeta) { - infiniteMeta.reset() + if (wasInfiniteMode && infiniteScrollActions) { + infiniteScrollActions.reset() } } - }, [setPerPage, setPage, perPage, infiniteMeta]) + }, [setPerPage, setPage, perPage, infiniteScrollActions]) // 그리고 onPaginationChange 함수도 수정 function onPaginationChange(updaterOrValue: Updater) { @@ -350,15 +375,17 @@ export function useDataTable({ } } } + // -------- 나머지 기존 로직들 -------- - function onSortingChange(updaterOrValue: Updater) { - if (typeof updaterOrValue === "function") { - const newSorting = updaterOrValue(sorting) as ExtendedSortingState - void setSorting(newSorting) - } else { - void setSorting(updaterOrValue as ExtendedSortingState) + function onSortingChange(updater: Updater) { + const next = + typeof updater === "function" ? updater(sorting) : updater + if (!isEqual(next, sorting)) { + resetInfiniteScroll() + void setSorting(next as ExtendedSortingState) } } + function onGroupingChange(updaterOrValue: Updater) { if (typeof updaterOrValue === "function") { -- cgit v1.2.3