この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは。データアナリティクス事業本部 サービスソリューション部の北川です。
チーム内でTanstack Tableを使用する機会がありました。自分はまだ使ってませんが、今後使う可能性や、キャッチアップも兼ねて簡単にAPIを触ってみました。
Tanstack Tableとは
Tanstack Tableは、テーブルの作成を容易に実装できるヘッドレスUIライブラリになります。ヘッドレスなので柔軟性が高いのが特徴です。React Tableから移行されたライブラリであり、TypeScriptへ置き換わっているのが大きな変更点になります。
フィルタリングや、ソート、グルーピングなど機能も豊富です。今回は、その中でもページネーションのAPIを触ってみました。
試してみた
今回は、Next.jsを使用して、プロジェクトを作成します。
$ npx create-next-app sample-table --ts
プロジェクトに移動し、Tanstack Tableをインストールします。
$ yarn add @tanstack/react-table
テーブル作成
適当なファイルを作成して、テーブルの中身にあたるDATAと、列にあたるCOLUMNSを定義します。columnsのHeaderにはテーブルのヘッダーの値、accessorにはオブジェクトのキーを指定します。
libs/data.ts
import { ColumnDef } from "@tanstack/react-table";
type TData = {
col1: string;
col2: string;
col3: number;
};
export const DATA: TData[] = [
{
col1: "Hello",
col2: "World",
col3: 1234,
},
{
col1: "react-table",
col2: "rocks",
col3: 5678,
},
{
col1: "whatever",
col2: "you want",
col3: 9012,
},
];
export const COLUMNS: ColumnDef<any>[] = [
{
header: "Column 1",
accessorKey: "col1",
},
{
header: "Column 2",
accessorKey: "col2",
},
{
header: "Column 3",
accessorKey: "col3",
},
];
useReactTable
フックを使用してテーブルをインスタンス化します。定義したDATAとCOLUMNSを、useReactTableに渡します。
pages/index.tsxを編集します。useMemoを使用して、データが不要に再レンダリングするのを防ぎます。
pages/index.tsx
import type { NextPage } from "next";
import styles from "../styles/Home.module.css";
import {
flexRender,
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table";
import { COLUMNS, DATA } from "../libs/data";
import { useMemo } from "react";
const Home: NextPage = () => {
const columns = useMemo(() => COLUMNS, []);
const data = useMemo(() => DATA, []);
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
});
return (
<div className={styles.container}>
<main className={styles.main}>
<table>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id} colSpan={header.colSpan}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => {
return (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => {
return (
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</main>
<p>{table.getRowModel().rows.length} Rows</p>
</div>
);
};
export default Home;
スタイリング
ヘッドレスなので、スタイルは完全にこちら側で制御します。今回は試すだけなので、global.cssファイルにcssを追記しました。
styles.global.css
table {
border: 1px solid lightgray;
padding: 1px;
}
tbody {
border-bottom: 1px solid lightgray;
}
th {
border-bottom: 1px solid lightgray;
padding: 4px;
}
td {
padding: 4px;
}
tfoot {
color: gray;
}
tfoot th {
font-weight: normal;
}
ページネーション
ページネーションの実装には、getPaginationRowModel
を使用します。
const table = useReactTable({
data: data,
columns: columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
ページネーションの機能だけでも、多くのメソッドが用意されています(以下は一部)。
- getCanPreviousPage, getCanNextPage : 前のページ、次のページに移動できるかどうか真偽値を返す
-
nextPage、previousPage : 次のページに移動、前のページに移動する
-
setPageIndex : 指定のインデックスまで移動する
-
setPageSize : テーブルの表示領域を指定する
-
getPageCount : 全体のページ数を返す
pages/index.tsxを以下のように変更してみました。今回はuseSWRと、JSONPlaceholderを使用して、仮データを用意しています。
pages/index.tsx
import type { NextPage } from "next";
import styles from "../styles/Home.module.css";
import {
ColumnDef,
flexRender,
getCoreRowModel,
getPaginationRowModel,
useReactTable,
} from "@tanstack/react-table";
import { useMemo } from "react";
import useSWR from "swr";
const fetcher = (args: string) => fetch(args).then((res) => res.json());
type TData = {
userId: number;
id: number;
title: string;
body: string;
};
const Home: NextPage = () => {
const { data, isLoading } = useSWR(
"https://jsonplaceholder.typicode.com/posts",
fetcher
);
const columns = useMemo<ColumnDef<TData>[]>(
() => [
{
header: "userId",
accessorKey: "userId",
},
{
header: "Id",
accessorKey: "id",
},
{
header: "Title",
accessorKey: "title",
},
],
[]
);
const table = useReactTable({
data: data,
columns: columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
debugTable: true,
});
if (!table || isLoading) return <></>;
return (
<main className={styles.main}>
<table>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id} colSpan={header.colSpan}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => {
return (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => {
return (
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
<div style={{ margin: "5px" }}>
<span>Page</span>
<strong>
{table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
</strong>
</div>
<div>
<button
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
{"<<"}
</button>
<button
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
{"<"}
</button>
<button
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
{">"}
</button>
<button
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
{">>"}
</button>
</div>
<select
style={{ margin: "10px" }}
value={table.getState().pagination.pageSize}
onChange={(e) => {
table.setPageSize(Number(e.target.value));
}}
>
{[10, 20, 30, 40, 50].map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
<div>{table.getRowModel().rows.length} Rows</div>
</main>
);
};
export default Home;
ページネーション機能も簡単に実装することができました。他にも、指定のページまで飛ぶためのメソッドなどが用意されています。
まとめ
今回は、Tanstack Tableを使用したテーブルの実装を試してみました。ドキュメントには、それぞれの機能を使った実装例ののsandboxが用意されているので、コードを通して使い方を学べるのはありがたかったです。
ではまた。