npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

data-table-pro

v0.1.0

Published

A type-safe, compound React data table with built-in React Query integration. More flexible than antd Table.

Downloads

140

Readme

DataTable Pro

一个类型安全、基于复合组件模式的 React 数据表格库,内置 React Query 集成。
比 Ant Design Table 更灵活、更可控、类型推导更严格。

npm version License: MIT


为什么用 DataTable Pro 而不是 Ant Design Table?

| 特性 | DataTable Pro | Ant Design Table | |------|--------------|------------------| | 类型安全 | 泛型 DataTable<T> 一路贯通到列定义、行数据、选中项,0 个 any | 大量 any / Record<string, any>,类型推导粗放 | | 复合组件模式 | <Table> / <Header> / <Body> / <Row> / <Cell> / <Pagination> 可自由组合 | 单一 <Table> 组件,所有逻辑通过 Props 塞入 | | 选中双模式 | 受控(外部 state)和非受控(内部 state)互不干扰,用 selectedRows === undefined 区分 | 仅受控,必须手动管理 | | React Query 内置 | <WithQuery> 壳组件直接集成,useMemo 稳定 queryKey,杜绝无限请求 | 无内置支持,需自行集成 | | 控件体积 | 按需加载,无额外 CSS 依赖(仅 Tailwind 基础类) | 全量引入 antd 样式,即使只用一个 Table | | 源码可控 | 千行级源码,完全可读、可 debug、可定制 | 万行级黑盒,定制成本高 | | 测试覆盖 | 核心逻辑 ≥ 90% 覆盖率,65%+ 分支覆盖 | 商业组件,测试不可见 |

设计权衡

  • 不提供「开箱即用」的 UI 主题:DataTable Pro 只提供基础样式(Tailwind utility),方便你快速覆盖。如果需要精美主题,直接套用自己项目的 Tailwind 配置即可。
  • 不内置「编辑表格」:聚焦在「展示型表格」场景,编辑功能留给用户通过 render 自定义列实现。
  • 不提供「虚拟滚动」:对于 1000+ 行数据,建议使用服务端分页 + <WithQuery> 方案。

安装

# pnpm(推荐)
pnpm add data-table-pro

# npm
npm install data-table-pro

# yarn
yarn add data-table-pro

依赖要求

  • react ≥ 18
  • react-dom ≥ 18
  • @tanstack/react-query ≥ 5(仅在使用 <WithQuery> 时需要)

快速上手

import { DataTable } from 'data-table-pro';
import type { Column } from 'data-table-pro';

interface User {
  id: number;
  name: string;
  age: number;
  role: string;
}

const columns: Column<User>[] = [
  { key: 'id', header: 'ID', sortable: true },
  { key: 'name', header: 'Name', sortable: true, filter: { type: 'text' } },
  { key: 'age', header: 'Age', sortable: true },
  { key: 'role', header: 'Role', filter: { type: 'select' } },
];

const data: User[] = [
  { id: 1, name: 'Alice', age: 30, role: 'engineer' },
  { id: 2, name: 'Bob', age: 25, role: 'designer' },
  { id: 3, name: 'Carol', age: 35, role: 'qa' },
];

export default function App() {
  return <DataTable<User> data={data} columns={columns} />;
}

完整 API 文档

DataTable<T> 主组件

| Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | data | readonly T[] | 必填 | 表格数据 | | columns | readonly Column<T>[] | 必填 | 列定义 | | selectedRows | readonly T[] | undefined | 传入即受控模式 | | onSelectionChange | (rows: readonly T[]) => void | - | 选中变化回调 | | enableSelection | boolean | 自动 | 是否显示复选框列 | | getRowKey | (row: T) => string \| number | row.idJSON.stringify | 行唯一标识 | | pageSize | number | 10 | 每页行数 | | children | ReactNode | - | 自定义子组件组合 |

Column<T> 列定义

| 属性 | 类型 | 必填 | 说明 | |------|------|------|------| | key | keyof T | 是 | 数据字段键 | | header | string | 是 | 表头显示文本 | | sortable | boolean | 否 | 是否可排序 | | filter | { type: 'text' \| 'select' \| 'range' } | 否 | 筛选配置 | | render | (row: T) => ReactNode | 否 | 自定义渲染 | | visible | boolean | 否 | 缺省为 true |

useDataTable<T> Hook

const {
  processedData,   // 排序+筛选+分页后的数据
  totalCount,      // 筛选后总条数
  pageCount,       // 总页数
  sort,            // 当前排序状态
  filter,          // 当前筛选状态
  pagination,      // 当前分页状态
  selection,       // 当前选中行
  visibleColumns,  // 可见列列表
  api,             // 操作方法
} = useDataTable<T>(options);

api 操作方法

| 方法 | 签名 | 说明 | |------|------|------| | toggleSort | (key: keyof T) => void | 三态切换排序 | | setFilter | (key: keyof T, value: unknown) => void | 设置筛选值 | | clearFilter | (key: keyof T) => void | 清除某字段筛选 | | setPage | (page: number) => void | 跳转页码 | | setPageSize | (size: number) => void | 修改每页行数 | | toggleColumnVisibility | (key: keyof T) => void | 切换列显隐 | | toggleSelect | (row: T) => void | 选中/取消某行 | | toggleSelectAll | () => void | 全选/反选当前页 | | setSelection | (rows: readonly T[]) => void | 精确设置选中行 | | isRowSelected | (row: T) => boolean | 判断某行是否选中 | | isAllSelected | () => boolean | 判断当前页是否全选 |

<WithQuery<T>> React Query 集成壳

import { useMemo } from 'react';
import { WithQuery } from 'data-table-pro';
import { QueryClientProvider } from '@tanstack/react-query';

const queryKey = useMemo(() => ['users', { page }], [page]);

<WithQuery<User>
  queryKey={queryKey}          // 必须用 useMemo 稳定引用(红线 #4)
  fetcher={() => fetchUsers(page)}
  enabled={true}
  loadingFallback={<div>加载中...</div>}
  errorFallback={(err, retry) => <button onClick={retry}>重试</button>}
>
  {({ data, totalCount, isLoading, isFetching, error, refetch }) => (
    <>
      {isFetching && <span>刷新中...</span>}
      <DataTable<User> data={data} columns={columns} />
    </>
  )}
</WithQuery>

Props

| Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | queryKey | QueryKey | 必填 | React Query 的 key,必须用 useMemo 固定 | | fetcher | () => Promise<WithQueryFetcherResult<T>> | 必填 | 数据拉取函数 | | enabled | boolean | true | 是否启用查询 | | loadingFallback | ReactNode | '加载中...' | 加载中 UI | | errorFallback | (err: Error, retry: () => void) => ReactNode | - | 错误 UI | | children | (props: WithQueryRenderProps<T>) => ReactNode | 必填 | 数据就绪后渲染 |


使用示例

1. 基础渲染

<DataTable<User> data={data} columns={columns} />

2. 排序 + 筛选 + 分页

<DataTable<User> data={data} columns={columns} pageSize={5} />

3. 非受控选中(多选)

<DataTable<User>
  data={data}
  columns={columns}
  enableSelection
  onSelectionChange={(rows) => console.log('选中:', rows)}
/>

4. 受控选中 + 批量删除

function App() {
  const [selected, setSelected] = useState<User[]>([]);
  const deleteSelected = () => {
    setData((prev) => prev.filter((u) => !selected.includes(u)));
    setSelected([]);
  };
  return (
    <>
      <button onClick={deleteSelected}>删除选中 ({selected.length})</button>
      <DataTable<User>
        data={data}
        columns={columns}
        selectedRows={selected}
        onSelectionChange={setSelected}
      />
    </>
  );
}

5. 服务端分页 + 排序

const [page, setPage] = useState(1);
const [sortKey, setSortKey] = useState<'id' | 'name'>('id');
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc');

const queryKey = useMemo(
  () => ['users', { page, sortKey, sortOrder }],
  [page, sortKey, sortOrder],
);
const fetcher = useCallback(
  () => fetchUsers({ page, sortKey, sortOrder }),
  [page, sortKey, sortOrder],
);

<WithQuery<User> queryKey={queryKey} fetcher={fetcher}>
  {({ data, totalCount }) => (
    <DataTable<User> data={data} columns={columns} />
  )}
</WithQuery>

6. 复合组件自定义布局

<DataTable<User> data={data} columns={columns}>
  <DataTable.Header />
  <DataTable.Body>
    <DataTable.Row index={0} />
    <DataTable.Row index={1} />
  </DataTable.Body>
  <DataTable.Pagination />
</DataTable>

红线约束

本组件库遵循以下核心设计原则(红线),违反会导致运行时错误或类型错误:

  1. 子组件须依附 <Table><Header> / <Body> / <Row> / <Cell> / <Pagination> 单独渲染会抛出 "[data-table-pro] 子组件必须在 <Table> 内使用" 错误。
  2. 泛型链路贯通DataTable<T>useDataTable<T>Column<T>0 个 any
  3. 选中双模式互斥selectedRows === undefined 走非受控内部 state,否则走受控,两种路径不共存。
  4. queryKey 必须 useMemo 固定<WithQuery>queryKey 若在渲染中新生成,会触发无限请求。
  5. 测试覆盖率 ≥ 60%:核心逻辑覆盖率 ≥ 90%,分支覆盖 ≥ 65%。

常见问题 (FAQ)

Q: 为什么我点击排序/筛选没有反应?

确保你传入了 sortable / filter 配置。useDataTable 的排序和筛选逻辑需要列定义中的对应字段。

Q: 选中行的复选框不显示?

需要设置 enableSelection 或传入 selectedRows / onSelectionChange

Q: 受控选中模式下,为什么勾选后复选框不自动打勾?

受控模式要求父组件更新 selectedRows 值。勾选动作只触发 onSelectionChange 回调,不会修改内部 state。

Q: 如何修改表格样式?

表格使用 Tailwind utility 类,覆盖非常方便:

<DataTable<User>
  data={data}
  columns={columns}
  className="your-custom-class"
/>

或者直接覆盖 CSS:

/* 表格行悬停 */
tr:hover { background-color: #f0f9ff; }

Q: 支持服务端排序/筛选吗?

支持。通过 <WithQuery> 把排序/筛选参数拼入 queryKey,每次变化自动触发新请求。

Q: 和 @tanstack/react-table 有什么区别?

@tanstack/react-table 是一个「无头 UI」库,你需要自己实现所有渲染逻辑。
DataTable Pro 提供开箱即用的复合组件,同时保留 useDataTable Hook 供自定义场景使用。


License

MIT