Skip to content

Commit ef5bb56

Browse files
committed
refactor(frontend): DataTable 컴포넌트에서 테이블 로직을 커스텀 훅으로 분리
정렬과 즐겨찾기 로직이 컴포넌트에 직접 포함되어 가독성이 떨어지고 재사용이 어려웠음 - useTableSort: 정렬 상태 관리 및 행 정렬 로직 분리 - useFavoriteRows: 즐겨찾기 행 분리 로직 추출 - getRowProps 타입을 ComponentPropsWithoutRef로 개선 fix #268
1 parent 1cc995d commit ef5bb56

4 files changed

Lines changed: 158 additions & 66 deletions

File tree

src/frontend/src/components/table/data-table.tsx

Lines changed: 24 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import { Box, EmptyState, HStack, Table } from "@chakra-ui/react";
2-
import { orderBy, partition } from "es-toolkit/array";
32
import { get } from "es-toolkit/compat";
4-
import { ReactNode, TableHTMLAttributes, useCallback, useMemo, useState } from "react";
3+
import {
4+
ComponentPropsWithoutRef,
5+
ReactNode,
6+
TableHTMLAttributes,
7+
useCallback,
8+
useMemo,
9+
useState,
10+
} from "react";
511

612
import {
713
PaginationItems,
@@ -11,6 +17,8 @@ import {
1117
} from "~/components/chakra/pagination";
1218

1319
import { FavoriteValue, getFavoriteRowStyles, isFavoriteValue } from "./favorite-control";
20+
import { useFavoriteRows } from "./hooks/use-favorite-rows";
21+
import { useTableSort } from "./hooks/use-table-sort";
1422
import { SortControl } from "./sort-control";
1523

1624
export type DataTableProps<T> = TableHTMLAttributes<HTMLTableElement> & {
@@ -21,7 +29,7 @@ export type DataTableProps<T> = TableHTMLAttributes<HTMLTableElement> & {
2129
};
2230
favoriteKeyPath?: string;
2331
favorites?: FavoriteValue[];
24-
getRowProps?: (row: { data: T; index: number }) => { [key: string]: any };
32+
getRowProps?: (row: { data: T; index: number }) => ComponentPropsWithoutRef<typeof Table.Row>;
2533
pagination?: boolean;
2634
rows: {
2735
data: T;
@@ -50,74 +58,24 @@ export const DataTable = <T,>({
5058
rows,
5159
...rest
5260
}: DataTableProps<T>) => {
53-
const [sortOrder, setSortOrder] = useState<"asc" | "desc" | null>(defaultSorting?.value || null);
54-
const [currentSortKey, setCurrentSortKey] = useState<string | null>(
55-
defaultSorting?.sortKey || null
56-
);
57-
58-
const displayRows = useMemo(() => {
59-
if (!rows.length) return [];
60-
61-
const hasFavoriteFeature = !!favoriteKeyPath && favorites.length > 0;
62-
63-
let favoriteRows: typeof rows = [];
64-
let normalRows: typeof rows = [];
65-
66-
if (hasFavoriteFeature) {
67-
[favoriteRows, normalRows] = partition(rows, (row) => {
68-
const value = get(row.data, favoriteKeyPath);
69-
return (
70-
(typeof value === "string" || typeof value === "number") && favorites.includes(value)
71-
);
72-
});
73-
} else {
74-
normalRows = [...rows];
75-
}
61+
const { currentSortKey, handleSort, sortOrder, sortRows } = useTableSort({
62+
columns,
63+
defaultSorting,
64+
});
7665

77-
const sortRows = (rowsToSort: typeof rows) => {
78-
if (!currentSortKey || !sortOrder) return rowsToSort;
79-
80-
return orderBy(
81-
rowsToSort,
82-
[
83-
(row) => {
84-
const column = columns.find((col) => col.sortKey === currentSortKey);
85-
if (column?.sortValue) {
86-
return column.sortValue(row.data);
87-
}
88-
return get(row.data, currentSortKey);
89-
},
90-
],
91-
[sortOrder]
92-
);
93-
};
66+
const { favoriteRows, hasFavoriteFeature, normalRows } = useFavoriteRows({
67+
favoriteKeyPath,
68+
favorites,
69+
rows,
70+
});
9471

72+
const displayRows = useMemo(() => {
9573
if (hasFavoriteFeature) {
96-
const sortedFavoriteRows = sortRows(favoriteRows);
97-
const sortedNormalRows = sortRows(normalRows);
98-
return [...sortedFavoriteRows, ...sortedNormalRows];
99-
}
100-
101-
return sortRows(rows);
102-
}, [rows, currentSortKey, sortOrder, columns, favoriteKeyPath, favorites]);
103-
104-
const handleSort = (column: Column<T>) => {
105-
if (!column.sortKey) return;
106-
107-
let newOrder: "asc" | "desc" | null;
108-
if (currentSortKey !== column.sortKey) {
109-
newOrder = "asc";
110-
} else if (sortOrder === "asc") {
111-
newOrder = "desc";
112-
} else if (sortOrder === "desc") {
113-
newOrder = null;
114-
} else {
115-
newOrder = "asc";
74+
return [...sortRows(favoriteRows), ...sortRows(normalRows)];
11675
}
11776

118-
setSortOrder(newOrder);
119-
setCurrentSortKey(newOrder ? column.sortKey : null);
120-
};
77+
return sortRows(normalRows);
78+
}, [favoriteRows, hasFavoriteFeature, normalRows, sortRows]);
12179

12280
const [currentPage, setCurrentPage] = useState(1);
12381
const pageSize = 10;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { getValueFromPath, useFavoriteRows } from "./use-favorite-rows";
2+
export { useTableSort } from "./use-table-sort";
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { partition } from "es-toolkit/array";
2+
import { get } from "es-toolkit/compat";
3+
import { useMemo } from "react";
4+
5+
import { FavoriteValue } from "../favorite-control";
6+
7+
const isFavoriteValue = (value: unknown): value is FavoriteValue => {
8+
return typeof value === "string" || typeof value === "number";
9+
};
10+
11+
type UseFavoriteRowsOptions<T> = {
12+
favoriteKeyPath?: string;
13+
favorites: FavoriteValue[];
14+
rows: Array<{ data: T }>;
15+
};
16+
17+
type UseFavoriteRowsReturn<T> = {
18+
favoriteRows: Array<{ data: T }>;
19+
hasFavoriteFeature: boolean;
20+
normalRows: Array<{ data: T }>;
21+
};
22+
23+
export const useFavoriteRows = <T,>({
24+
favoriteKeyPath,
25+
favorites,
26+
rows,
27+
}: UseFavoriteRowsOptions<T>): UseFavoriteRowsReturn<T> => {
28+
return useMemo(() => {
29+
const hasFavoriteFeature = !!favoriteKeyPath && favorites.length > 0;
30+
31+
if (!hasFavoriteFeature) {
32+
return {
33+
favoriteRows: [],
34+
hasFavoriteFeature: false,
35+
normalRows: [...rows],
36+
};
37+
}
38+
39+
const [favoriteRows, normalRows] = partition(rows, (row) => {
40+
const value = get(row.data, favoriteKeyPath);
41+
return isFavoriteValue(value) && favorites.includes(value);
42+
});
43+
44+
return {
45+
favoriteRows,
46+
hasFavoriteFeature,
47+
normalRows,
48+
};
49+
}, [favoriteKeyPath, favorites, rows]);
50+
};
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { orderBy } from "es-toolkit/array";
2+
import { get } from "es-toolkit/compat";
3+
import { useCallback, useMemo, useState } from "react";
4+
5+
type SortOrder = "asc" | "desc" | null;
6+
7+
type UseTableSortOptions<T> = {
8+
columns: Array<{
9+
sortKey?: string;
10+
sortValue?: (data: T) => number | string | null;
11+
}>;
12+
defaultSorting?: {
13+
sortKey: string;
14+
value: "asc" | "desc";
15+
};
16+
};
17+
18+
type UseTableSortReturn<T> = {
19+
currentSortKey: string | null;
20+
handleSort: (column: { sortKey?: string }) => void;
21+
sortOrder: SortOrder;
22+
sortRows: <R extends { data: T }>(rows: R[]) => R[];
23+
};
24+
25+
export const useTableSort = <T,>({
26+
columns,
27+
defaultSorting,
28+
}: UseTableSortOptions<T>): UseTableSortReturn<T> => {
29+
const [currentSortKey, setCurrentSortKey] = useState<string | null>(
30+
defaultSorting?.sortKey || null
31+
);
32+
const [sortOrder, setSortOrder] = useState<SortOrder>(defaultSorting?.value || null);
33+
34+
const handleSort = useCallback(
35+
(column: { sortKey?: string }) => {
36+
if (!column.sortKey) return;
37+
38+
let newOrder: SortOrder;
39+
if (currentSortKey !== column.sortKey) {
40+
newOrder = "asc";
41+
} else if (sortOrder === "asc") {
42+
newOrder = "desc";
43+
} else if (sortOrder === "desc") {
44+
newOrder = null;
45+
} else {
46+
newOrder = "asc";
47+
}
48+
49+
setCurrentSortKey(newOrder ? column.sortKey : null);
50+
setSortOrder(newOrder);
51+
},
52+
[currentSortKey, sortOrder]
53+
);
54+
55+
const sortRows = useMemo(() => {
56+
const column = columns.find((col) => col.sortKey === currentSortKey);
57+
58+
return <R extends { data: T }>(rows: R[]): R[] => {
59+
if (!currentSortKey || !sortOrder) return rows;
60+
61+
return orderBy(
62+
rows,
63+
[
64+
(row) => {
65+
if (column?.sortValue) {
66+
return column.sortValue(row.data);
67+
}
68+
return get(row.data, currentSortKey);
69+
},
70+
],
71+
[sortOrder]
72+
);
73+
};
74+
}, [columns, currentSortKey, sortOrder]);
75+
76+
return {
77+
currentSortKey,
78+
handleSort,
79+
sortOrder,
80+
sortRows,
81+
};
82+
};

0 commit comments

Comments
 (0)