feat: useDataTable 훅 생성

This commit is contained in:
2025-06-18 15:24:07 +09:00
parent 1309767de9
commit 6d6892973f
3 changed files with 115 additions and 0 deletions
@@ -0,0 +1,65 @@
import { type UseQueryResult } from "@tanstack/react-query";
import type { PageableList } from "../model";
/**
* Match the state of a query to a set of components.
*
* Useful for rendering different UI based on the state of a query.
*/
export function matchQueryStatus<T>(
query: UseQueryResult<PageableList<T>>,
options: {
Loading?: JSX.Element;
Errored?: JSX.Element | ((error: unknown) => JSX.Element);
Empty?: JSX.Element;
Success: (
data: UseQueryResult<PageableList<T>> & {
data: NonNullable<UseQueryResult<T>["data"]>;
},
) => JSX.Element;
},
): JSX.Element;
export function matchQueryStatus<T>(
query: UseQueryResult<PageableList<T>>,
options: {
Loading?: JSX.Element;
Errored?: JSX.Element | ((error: unknown) => JSX.Element);
Success: (data: UseQueryResult<PageableList<T>>) => JSX.Element;
},
): JSX.Element;
export function matchQueryStatus<T>(
query: UseQueryResult<PageableList<T>>,
{
Loading,
Errored,
Empty,
Success,
}: {
Loading?: JSX.Element;
Errored?: JSX.Element | ((error: unknown) => JSX.Element);
Empty?: JSX.Element;
Success: (data: UseQueryResult<PageableList<T>>) => JSX.Element;
},
): JSX.Element {
if (query.isLoading && Loading) {
return Loading;
}
if (query.isError && Errored) {
if (typeof Errored === "function") {
return Errored(query.error);
}
return Errored;
}
const isEmpty =
query.data?.content === undefined ||
query.data?.content === null ||
(Array.isArray(query.data?.content) && query.data?.content.length === 0);
if (isEmpty && Empty) {
return Empty;
}
return Success(query);
}
+33
View File
@@ -0,0 +1,33 @@
import type { HTMLAttributes } from "react";
import type { UseQueryResult } from "@tanstack/react-query";
import { useReactTable, type TableOptions } from "@tanstack/react-table";
import { DataTable } from "@common/components";
import { NoData } from "../ui/no-data";
import type { PageableList } from "../model";
import { matchQueryStatus } from "./match-query-status";
type DataTableOptions<T> = {
variant?: "default" | "secondary" | "line";
className?: HTMLAttributes<HTMLTableElement>["className"];
viewPagination?: boolean;
onRowClick?: (row: T) => void;
};
export const useDataTable = <T,>(
query: UseQueryResult<PageableList<T>>,
tableOptions: Omit<TableOptions<T>, "data" | "rowCount">,
dataTableOptions?: DataTableOptions<T>,
) => {
const table = useReactTable({
...tableOptions,
data: query.data?.content ?? [],
rowCount: query.data?.totalElements ?? 0,
});
return matchQueryStatus(query, {
Empty: <NoData />,
Success: () => {
return <DataTable table={table} {...dataTableOptions} />;
},
});
};
+17
View File
@@ -0,0 +1,17 @@
import { cn } from "@common/components";
function NoData({ className }: { className?: string }) {
return (
<div
className={cn(
"flex h-96 flex-col items-center justify-center gap-4",
className,
)}
>
<i className="diveicon di-info-circle text-3xl" />
조회된 데이터가 없습니다.
</div>
);
}
export { NoData };