🏓
【React】react-tableを試す
react-table
TanStack/table: 🤖 Headless UI for building powerful tables & datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table
Reactでtable表示のUIを実装する際に便利な↑こちらのパッケージを試してみたいと思います。
他にも solid
や vue
、svelte
用のパッケージも存在しています。
動作環境
node: v16
react: v18.2.0
react-dom: v18.2.0
@chakra-ui/react: v2.3.1
インストール
$ yarn add @tanstack/react-table
ベーシックな実装
まずは一番シンプルな実装を試してみたいと思います。
import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
import React from 'react';
type Book = {
title: string;
author: string;
};
const books: Book[] = [
{
title: 'ハリー・ポッターと賢者の石',
author: 'J.K.ローリング',
},
{
title: 'こころ',
author: '夏目漱石',
},
];
const columns: ColumnDef<Book, any>[] = [
{
accessorKey: 'title',
header: 'タイトル',
},
{
accessorKey: 'author',
header: '著者',
},
];
export const BasicTable: React.FC = () => {
const table = useReactTable<Book>({
data: books,
columns,
getCoreRowModel: getCoreRowModel(),
});
return (
<div>
<table>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
};
↑を実行すると以下のテーブルが表示されます。シンプルすぎで味気ないので次は装飾を追加してみます。
装飾を追加
今回は chakra-ui のTableを使ってみたいと思います。
BasicTable
の return を以下に修正します。
import { Table, TableContainer, Tbody, Td, Th, Thead, Tr } from '@chakra-ui/react';
...
export const BasicTable: React.FC = () => {
const table = useReactTable<Book>({
data: books,
columns,
getCoreRowModel: getCoreRowModel(),
});
return (
<TableContainer>
<Table>
<Thead>
{table.getHeaderGroups().map((headerGroup) => (
<Tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<Th key={header.id}>
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
</Th>
))}
</Tr>
))}
</Thead>
<Tbody>
{table.getRowModel().rows.map((row) => (
<Tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<Td key={cell.id} borderX="1px solid #e2e8f0">
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</Td>
))}
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
);
};
↑を実行すると以下みたくテーブルっぽくなりました ✨
編集できるようにする
今度はタイトル部分のみ編集できるようにしてみたいと思います。
react-tableではmetaデータを扱える様になっており、自由にカスタム可能なので、
編集された際に呼ばれるCallbackをmetaデータ(TableMeta
)内に定義してそちらを呼ぶようしてみたいと思います。まずは TableMeta
を拡張する実装を追加します。
import { RowData } from '@tanstack/table-core';
declare module '@tanstack/table-core' {
interface TableMeta<TData extends RowData> {
updateData: (rowIndex: number, columnId: string, value: unknown) => void;
}
}
次に編集部分のコンポーネントを以下内容で作成します。
const defaultColumn: Partial<ColumnDef<Book>> = {
cell: ({ getValue, row: { index }, column: { id }, table }) => {
const initialValue = getValue();
if (id !== 'title') {
return <>{initialValue}</>;
}
// eslint-disable-next-line react-hooks/rules-of-hooks
const [value, setValue] = useState(initialValue);
const onBlur = () => {
table.options.meta?.updateData(index, id, value);
};
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
setValue(initialValue);
}, [initialValue]);
return <Input value={value as string} onChange={(e) => setValue(e.target.value)} onBlur={onBlur} />;
},
};
最後に useReactTable
部分を以下に修正します。
const table = useReactTable<Book>({
data: books,
columns,
defaultColumn,
getCoreRowModel: getCoreRowModel(),
meta: {
updateData: (index: number, columnId: string, value: any) => {
console.log(`table update data index:`, index, 'columnId:', columnId, 'value:', value);
},
},
});
metaデータ内のupdateDataで編集された時のCallbackが受け取れるようになりました。
ここまでで動作させてみると以下の様に、編集できるようになっています。
Discussion