summaryrefslogtreecommitdiff
path: root/components/data-table
diff options
context:
space:
mode:
Diffstat (limited to 'components/data-table')
-rw-r--r--components/data-table/data-table-filter-list.tsx44
-rw-r--r--components/data-table/data-table-grobal-filter.tsx4
-rw-r--r--components/data-table/data-table-view-options.tsx12
-rw-r--r--components/data-table/data-table.tsx7
4 files changed, 52 insertions, 15 deletions
diff --git a/components/data-table/data-table-filter-list.tsx b/components/data-table/data-table-filter-list.tsx
index 6088e912..ea4b1f90 100644
--- a/components/data-table/data-table-filter-list.tsx
+++ b/components/data-table/data-table-filter-list.tsx
@@ -66,6 +66,7 @@ import {
} from "@/components/ui/sortable"
import { useParams } from 'next/navigation';
import { useTranslation } from '@/i18n/client'
+import deepEqual from "fast-deep-equal"
interface DataTableFilterListProps<TData> {
table: Table<TData>
@@ -78,6 +79,10 @@ interface DataTableFilterListProps<TData> {
onFiltersChange?: (filters: Filter<TData>[], joinOperator: JoinOperator) => void
}
+export function isSame(a: unknown, b: unknown) {
+ return JSON.stringify(a) === JSON.stringify(b)
+}
+
export function DataTableFilterList<TData>({
table,
filterFields,
@@ -88,6 +93,11 @@ export function DataTableFilterList<TData>({
onFiltersChange,
}: DataTableFilterListProps<TData>) {
+ const prevRef = React.useRef<{
+ filters: Filter<TData>[]
+ join: JoinOperator
+ } | null>(null)
+
const params = useParams();
const lng = params ? (params.lng as string) : 'en';
@@ -114,13 +124,25 @@ export function DataTableFilterList<TData>({
})
)
+ const safeSetFilters = React.useCallback(
+ (next: Filter<TData>[] | ((p: Filter<TData>[]) => Filter<TData>[])) => {
+ setFilters((prev) => {
+ const value = typeof next === "function" ? next(prev) : next
+ return deepEqual(prev, value) ? prev : value // <─ 달라진 게 없으면 그대로
+ })
+ },
+ [setFilters]
+ )
+
+
+
// ✅ 외부 필터가 전달되면 URL 상태를 업데이트
React.useEffect(() => {
- if (externalFilters && externalFilters.length > 0) {
+ if (externalFilters && !deepEqual(externalFilters, filters)) {
console.log("=== 외부 필터 적용 ===", externalFilters);
- setFilters(externalFilters);
+ safeSetFilters(externalFilters);
}
- }, [externalFilters, setFilters]);
+ }, [externalFilters, setFilters, safeSetFilters]);
React.useEffect(() => {
if (externalJoinOperator) {
@@ -130,12 +152,20 @@ export function DataTableFilterList<TData>({
}, [externalJoinOperator, setJoinOperator]);
// ✅ 필터 변경 시 부모에게 알림
+
React.useEffect(() => {
- if (onFiltersChange) {
- onFiltersChange(filters, joinOperator);
+ const prev = prevRef.current
+ const changed =
+ !prev ||
+ !deepEqual(prev.filters, filters) ||
+ prev.join !== joinOperator
+
+ if (changed) {
+ prevRef.current = { filters, join: joinOperator }
+ onFiltersChange?.(filters, joinOperator)
}
- }, [filters, joinOperator, onFiltersChange]);
-
+ }, [filters, joinOperator, onFiltersChange])
+
const debouncedSetFilters = useDebouncedCallback(setFilters, debounceMs)
function addFilter() {
diff --git a/components/data-table/data-table-grobal-filter.tsx b/components/data-table/data-table-grobal-filter.tsx
index a1f0a6f3..ca60bf02 100644
--- a/components/data-table/data-table-grobal-filter.tsx
+++ b/components/data-table/data-table-grobal-filter.tsx
@@ -24,8 +24,8 @@ export function DataTableGlobalFilter() {
// Debounced callback that sets the URL param after `delay` ms
const debouncedSetSearch = useDebouncedCallback((value: string) => {
- setSearchValue(value)
- }, 300) // 300ms or chosen delay
+ if (value !== searchValue) setSearchValue(value.trim() === "" ? undefined : value);
+ }, 300)
// When user types, update local `tempValue` immediately,
// then call the debounced function to update the query param
diff --git a/components/data-table/data-table-view-options.tsx b/components/data-table/data-table-view-options.tsx
index 422e3065..b689adab 100644
--- a/components/data-table/data-table-view-options.tsx
+++ b/components/data-table/data-table-view-options.tsx
@@ -39,6 +39,7 @@ import {
} from "@/components/ui/sortable"
import { useTranslation } from '@/i18n/client'
import { useParams, usePathname } from "next/navigation";
+import deepEqual from "fast-deep-equal"
/**
@@ -70,6 +71,7 @@ export function DataTableViewOptions<TData>({
}: DataTableViewOptionsProps<TData>) {
const triggerRef = React.useRef<HTMLButtonElement>(null)
+
const params = useParams();
const lng = params?.lng as string;
const { t } = useTranslation(lng);
@@ -115,11 +117,11 @@ export function DataTableViewOptions<TData>({
const finalOrder = [...nonHideable, ...columnOrder]
// Now we set the table's official column order
- table.setColumnOrder(finalOrder)
-
- // Reset auto-size when column order changes
- resetAutoSize?.()
- }, [columnOrder, hideableCols, table, resetAutoSize])
+ if (!deepEqual(table.getState().columnOrder, finalOrder)) {
+ table.setColumnOrder(finalOrder)
+ resetAutoSize?.()
+ }
+ }, [columnOrder, hideableCols.join("|"), table, resetAutoSize])
return (
diff --git a/components/data-table/data-table.tsx b/components/data-table/data-table.tsx
index 33fca5b8..b898c2ea 100644
--- a/components/data-table/data-table.tsx
+++ b/components/data-table/data-table.tsx
@@ -66,9 +66,14 @@ export function DataTable<TData>({
[compact]
);
+ const stableChildren = React.useMemo(() => {
+ console.log("📦 DataTable children 메모이제이션됨");
+ return children;
+ }, [children]);
+
return (
<div className={cn("w-full space-y-2.5 overflow-auto", className)} {...props}>
- {children}
+ {stableChildren}
<div className="max-w-[100vw] overflow-auto" style={{ maxHeight: maxHeight || '35rem' }} >
<Table className="[&>thead]:sticky [&>thead]:top-0 [&>thead]:z-10 table-fixed">
{/* 테이블 헤더 */}