发布于 2026-01-06 0 阅读
0

使用 react-table 构建 React Table 组件

使用 react-table 构建 React Table 组件

如果你正在构建内部工具——管理面板、仪表盘、CRM等等——你很可能正在考虑如何构建一个表格组件来显示、编辑和操作数据。如果你使用 React,那么(幸运的是)你无需从头开始构建:react-table库提供了 Hooks,可以让你快速搭建并运行表格。

看完本教程,你将学会如何:

  • 使用 React 构建一个简单的表格,并修改数据、列和标题。
  • 通过使用styled-componentsReact 组件库或借用 React 组件库,将 CSS 传递给每个组件,从而为表格创建自定义 UI 。
  • 为表格添加排序、筛选和分页等更多功能。

自学这些东西可能很复杂。好在 react-table 库非常棒(我们也制作了这份指南来帮助你)。

简介:react-table

React-table 是一个专门用于在 React 中构建表格的开源库(顾名思义)。该库在 GitHub 上拥有超过 11.5 万颗星,并被众多知名科技公司使用,例如 Google、Apple 和 Microsoft。此外,我们 Retool 也非常欣赏它,因此我们赞助了它。

我们喜欢 react-table,因为它易于设置、自定义和扩展。该库涵盖了实用表格的基本功能——排序、筛选和分页——而且还深入提供了许多高级功能,例如:

  • 分组
  • 扩展状态
  • 自定义插件钩子

需要注意的是,react-table 是一个“无头”UI库。该库实际上并不渲染用户界面。这听起来可能有些奇怪,但这样的设计是为了让您更好地控制 react-table 组件的外观和风格,同时保持包的大小较小。别担心,添加 UI 非常简单,我们稍后会介绍。

既然我们主要讨论内部工具,那么假设我们要创建一个表格来显示客服代表的订单信息。我们的表格需要显示每位客户购买的客户信息(姓名和地址)以及订单信息(订单号和日期)。

完成本教程后,您将拥有五个使用 react-table 构建的表格版本:简单版、样式版、可排序版、可筛选版和分页版。下图是我们最终要实现的分页版本。

Retool-React-Table-Pagination

虽然看起来还可以更好,但这就是 CSS 的作用!

使用 react-table 构建一个简单的表格

首先,我们将创建一个基本表格来显示数据,不添加任何样式或其他功能。我们的客户支持代表需要一种简便的方法来查看每位客户的订单信息。这个简单的表格将包含两个顶级标题:“用户信息”“订单信息”。在“用户信息”下,我们需要两个二级标题分别显示每位客户的姓名地址。在“订单信息”下,我们还需要两个二级标题分别显示订单日期和订单号

在本节中,我们将创建一个包含四列并分为两组的表格。我们将详细讲解如何定义列对象和数据的形状、解析表头组,以及如何填充行和单元格。最终,您应该会看到类似这样的结果:

Retool-React-Table
请注意,我们在此部分添加了一些额外的样式,因此表格中有线条。

首先,我们需要完成一些基本准备工作,你需要使用包管理器(Yarn 或 npm)安装 react-table,并将该库导入到你的 React 应用中:

import { useTable } from 'react-table';
Enter fullscreen mode Exit fullscreen mode

安装并导入 react-table 之后,就可以通过useTableHook 定义数据和列了。react-table 利用了Hooks,这是 React 16.8 版本中新增的功能。如果您还不熟悉 React Hooks,我们建议您阅读 React 的Hooks 速览文档。

我们表格最重要的 Hook 是useTable。我们将向其传递两个参数useTable

  1. data = 使用useMemoHook 定义的表数据(数据必须先进行记忆化处理useTable才能传递给函数,以防止重复运行未更改的数据,从而减少计算时间)
const data = React.useMemo(() =>
 [
 {
 name: 'Kim Parrish',
 address: '4420 Valley Street, Garnerville, NY 10923',
 date: '07/11/2020',
 order: '87349585892118',
 },
 {
 name: 'Michele Castillo',
 address: '637 Kyle Street, Fullerton, NE 68638',
 date: '07/11/2020',
 order: '58418278790810',
 },
 {
 name: 'Eric Ferris',
 address: '906 Hart Country Lane, Toccoa, GA 30577',
 date: '07/10/2020',
 order: '81534454080477',
 },
 {
 name: 'Gloria Noble',
 address: '2403 Edgewood Avenue, Fresno, CA 93721',
 date: '07/09/2020',
 order: '20452221703743',
 },
 {
 name: 'Darren Daniels',
 address: '882 Hide A Way Road, Anaktuvuk Pass, AK 99721',
 date: '07/07/2020',
 order: '22906126785176',
 },
 {
 name: 'Ted McDonald',
 address: '796 Bryan Avenue, Minneapolis, MN 55406',
 date: '07/07/2020',
 order: '87574505851064',
 },
 ],
 []
)
Enter fullscreen mode Exit fullscreen mode
  1. columns = 使用useMemoHook 定义的列定义(列定义必须先进行记忆化才能传递给 Hook useTable)。
const columns = React.useMemo(
 () => [
 {
 Header: 'User Info',
 columns: [
 {
 Header: 'Name',
 accessor: 'name',
 },
 {
 Header: 'Address',
 accessor: 'address',
 },
 ],
 },
 {
 Header: 'Order Info',
 columns: [
 {
 Header: 'Date',
 accessor: 'date',
 },
 {
 Header: 'Order #',
 accessor: 'order',
 },
 ],
 },
 ],
 []
)
Enter fullscreen mode Exit fullscreen mode

请花点时间了解数据之间的关系。accessor中的“键”是数据对象中的“键” 。这对于我们在使用 `.` 时能够访问每一列的正确数据至关重要useTable

定义好数据之后,就可以实现我们的useTableHook 了。将数据列传递给 Hook useTable,它会返回一些属性,我们可以提取这些属性来构建表格 UI。

const {
 getTableProps,
 getTableBodyProps,
 headerGroups,
 rows,
 prepareRow,
} = useTable({ columns, data })
Enter fullscreen mode Exit fullscreen mode

现在,我们将使用这些提取的属性,通过熟悉的 JSX 标签(、、、<table>来构建我们的表格然后填充来自的属性<thead><tr><th><tbody>useTable

桌子

首先,我们需要<table>封装其余代码,并且需要传入该getTableProps()函数来解析任何表格属性。

<table {...getTableProps()}>
 ...
</table>
Enter fullscreen mode Exit fullscreen mode

标题

当我们开始构建表头时,事情就开始变得有点复杂了!简单来说,我们所做的就是使用上面定义的列标题名称来创建表头行。在深入代码之前,让我们先看一下渲染后的表格,以便更好地理解:

重新工具标题组

上表中每个带圆圈的部分都是一个headerGroup ,它是一个包含该行标题数组的对象。在这个表格中,我们将有两个标题行:红色圆圈标记的标题是第一个 headerGroup,蓝色圆圈标记的标题是第二个 headerGroup。

为了获取构建这些基于 headerGroup 的标头所需的数据,我们将使用 JavaScript 的map()方法。如果您不熟悉该方法,请花点时间阅读文档

首先,我们有<thead>标签,这很简单。在这个标签内,我们将使用它map()来解析每个 headerGroup,使用 `get` 创建一个新行,<tr>并将该 headerGroup 的getHeaderGroupProps()方法传递给它。

{headerGroups.map(headerGroup => (
   <tr {...headerGroup.getHeaderGroupProps()}>
     ...
   </tr>
))}
Enter fullscreen mode Exit fullscreen mode

在内部<tr>,我们map()再次使用它,但这次是对标头数组。每个标头对象都有一个Header属性(这是您将为每个标头指定的名称)、一个render()函数和另一个名为的属性解析器函数getHeaderProps()

对于每一列,我们使用<th>标签来创建列,确保传递该列的 prop 解析器函数getHeaderProps(),然后使用该render()函数来访问标题。

<thead>
 {headerGroups.map(headerGroup => (
   <tr {...headerGroup.getHeaderGroupProps()}>
     {headerGroup.headers.map(column => (
       <th {...column.getHeaderProps()}>{column.render('Header')}</th>
     ))}
   </tr>
 ))}
</thead>
Enter fullscreen mode Exit fullscreen mode

桌体

<table>之前一样<thead>,我们添加<tbody>并传入 prop 解析器函数getTableBodyProps()。然后,我们使用它map()来遍历rows ,它是一个Row对象数组。每个Row对象都有一个cells字段,它只是一个Cell对象数组,其中包含该行中每个单元格的数据。

Retool-React-Table-Component-Table-Body

橙色圆圈表示一行,粉色圆圈表示一个单元格。

对于每一行,我们需要将行对象传递给prepareRow()函数,这有助于高效渲染。接下来,我们返回<tr>用于渲染行的标签。在每个标签中<tr>,我们再次使用map()`parseCell` 来解析单元格。对于每个单元格,我们创建一个<td>标签,传入 prop 解析函数getCellProps(),然后使用 `renderCell`render()方法渲染单元格数据。

<tbody {...getTableBodyProps()}>
 {rows.map(row => {
   prepareRow(row)
   return (
     <tr {...row.getRowProps()}>
       {row.cells.map(cell => {
         return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
       })}
     </tr>
   )
 })}
</tbody>
Enter fullscreen mode Exit fullscreen mode

让我们把所有这些结合起来,渲染出我们的表格。

return (
 <table {...getTableProps()}>
   <thead>
     {headerGroups.map(headerGroup => (
       <tr {...headerGroup.getHeaderGroupProps()}>
         {headerGroup.headers.map(column => (
           <th {...column.getHeaderProps()}>{column.render('Header')}</th>
         ))}
       </tr>
     ))}
   </thead>
   <tbody {...getTableBodyProps()}>
     {rows.map(row => {
       prepareRow(row)
       return (
         <tr {...row.getRowProps()}>
           {row.cells.map(cell => {
             return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
           })}
         </tr>
       )
     })}
   </tbody>
 </table>
)
Enter fullscreen mode Exit fullscreen mode

使用上述代码,最终会得到如下所示的渲染表格:

Retool-Simple-React-Table-Final

为您的表格定制用户界面

即使你开发的工具仅供内部团队使用,用户界面美观(至少不至于糟糕)仍然至关重要。对于 react-table 来说,样式(至少是基础样式)尤为重要,因为该库实际上并不渲染任何组件。如果没有样式,最终得到的表格可能如下所示:

Retool-Simple-Unstyled-React-Table

你可以通过创建自定义样式或使用 React 组件库来设置 react-table 组件的样式。本节的最终效果如下所示:

Retool-React-Table-Styled

真漂亮,不是吗?

使用style道具

使用 react-table 设置表格样式非常简单,只需将 CSS 代码传递给style每个组件的 prop 即可。我们来看一下<th>用于设置表头行样式的标签:

<th
 {...column.getHeaderProps()}
 style={{
   borderBottom: 'solid 3px blue',
   background: 'green',
   color: 'white',
   fontWeight: 'bold',
 }}
>
Enter fullscreen mode Exit fullscreen mode

您也可以使用 CSS 文件和 CSS 模块。更多信息请查看 React 的CSS 文档。

使用styled-components

styled-components是一个很棒的 React 库,它允许你直接在 JS 代码中使用 CSS 为 React 组件设置样式(而不是使用外部 CSS 文件)。最近,它已经成为 React 中处理组件样式的一种非常流行的方式,所以你可能想把它用在你的表格上。

要使用该库styled-components,请先安装并将其导入到您的项目中。创建一个组件,该组件使用库中的样式表来创建带有 CSS 表格样式的元素。将创建该组件的所有代码移到一个单独的函数中。然后,在您的函数(定义列和数据的地方)中,返回渲染好的元素。这将把库中的样式应用到您的表格上。Stylesstyledstyled-componentsdivTableApp<Styles><Table>styled-components

import styled from 'styled-components'

/* Pssst this has the rest of the styling we've been using
to give our table borders and non-ugly spacing */

const Styles = styled.div`
 table {
   border-spacing: 0;
   border: 1px solid black;

   tr {
     :last-child {
       td {
         border-bottom: 0;
       }
     }
   }

   th,
   td {
     padding: 0.5rem;
     border-bottom: 1px solid black;
     border-right: 1px solid black;

     :last-child {
       border-right: 0;
     }
   }

   th {
     background: green;
     border-bottom: 3px solid blue;
     color: white;
     fontWeight: bold;
   }
 }
`

function Table({ columns, data }) {
 const {
   getTableProps,
   getTableBodyProps,
   headerGroups,
   rows,
   prepareRow,
 } = useTable({
   columns,
   data,
 })

 // Render the UI for your table
 return (
   <table {...getTableProps()} >
     ...
   </table>
 )
}

function App() {
 const columns = React.useMemo(...)

 const data = React.useMemo(...)

 return (
   <Styles>
     <Table columns={columns} data={data} />
   </Styles>
 )
}

export default App
Enter fullscreen mode Exit fullscreen mode

使用 React 组件库

如果你不想自己编写样式,使用 React 组件库是最佳选择。在这个例子中,我们将使用material-uireact-table 库创建一个漂亮的表格。

以上面的单元格样式示例为例,我们只需TableCell@material-ui/core/TableCell.

import TableCell from '@material-ui/core/TableCell'
...
<TableCell {...cell.getCellProps()}>
   {cell.render('Cell')}
</TableCell>
Enter fullscreen mode Exit fullscreen mode

TableCell这样就能应用组件的所有样式material-ui。您无需进行任何额外操作!

点击这里查看此代码的完整沙盒版本。

扩展您的表格,添加更多功能

任何值得渲染的表格都不会像我们的例子那样只有两列三行。大多数情况下,你会遇到很多列和很多行的数据。你需要一些功能来帮助用户浏览所有这些数据,例如排序、筛选和分页。

排序

我们希望客服代表能够轻松找到所需信息,而排序功能正是实现这一目标的绝佳途径!如果客服代表想要查看最新订单,他们可以按“日期”列中的日期进行排序。如果他们想要按字母顺序浏览客户信息,他们可以按“姓名”列中的姓名进行排序

排序功能是通过使用useSortByreact-table 的 Hook 实现的。请务必将其添加到您的导入语句中:

import { useTable, useSortBy } from 'react-table' 
Enter fullscreen mode Exit fullscreen mode

接下来,你需要将参数传递useSortByuseTableHook:

const {
 getTableProps,
 headerGroups,
 rows,
 getRowProps,
 prepareRow,
} = useTable(
 {
   columns,
   data,
 },
 useSortBy,
)
Enter fullscreen mode Exit fullscreen mode

如果您希望排序依据除默认字母数字值之外的其他值,则需要columns使用字段更新您的定义sortType。排序选项包括:

  • alphanumeric= 最适合对字母和数字进行排序(默认)
  • basic最适合对 0 到 1 之间的数字进行排序
  • datetime= 最适合按日期排序

在这个例子中,我们将使用默认值,但如果您需要添加代码,它看起来会像这样:

const columns = React.useMemo(
 () => [
   {
     Header: 'Rating',
     accessor: 'rating',
     sortType: 'basic',
   },
 ],
 []
)
Enter fullscreen mode Exit fullscreen mode

现在还有两件事要做。首先,将该getSortByToggleProps()函数传递给你的getHeaderProps()函数。该getSortByToggleProps()函数用于解析用户点击标题时切换排序方向的属性。

其次,span在列标题中添加一个标签,以显示向上箭头、向下箭头或不显示任何内容,从而指示该列的排序方式。您可以通过检查列属性来确定列的排序isSorted方式isSortedDesc

<thead>
 {headerGroups.map(headerGroup => (
   <tr {...headerGroup.getHeaderGroupProps()}>
     {headerGroup.headers.map(column => (
       <th {...column.getHeaderProps(column.getSortByToggleProps())}>
         {column.render('Header')}
         <span>
           {column.isSorted ? (column.isSortedDesc ? ' 🔽' : ' 🔼') : ''}
         </span>
       </th>
     ))}
   </tr>
 ))}
</thead>
Enter fullscreen mode Exit fullscreen mode

Retool-Styling-React-Table

请查看此代码沙箱,了解使用 react-table 进行排序的更高级版本。

过滤

为了简化操作,本教程将重点介绍如何在简单表格的列中添加文本筛选器。这将使我们的客户支持代表能够快速找到所需信息。如果客户联系他们,代表可以轻松地在“姓名”列中搜索该客户以查找其订单,或者在“订单号”列中搜索特定订单。

有关各种不同类型过滤器(包括非常有用的全局过滤器)的更多示例,请查看react-table 文档

筛选功能是通过使用useFilters()react-table 的 Hook 实现的。请务必将其添加到您的导入语句中:

import { useTable, useFilters } from 'react-table' 
Enter fullscreen mode Exit fullscreen mode

接下来,你需要将参数传递useFiltersuseTableHook:

const {
 getTableProps,
 headerGroups,
 rows,
 getRowProps,
 prepareRow,
} = useTable(
 {
   columns,
   data,
 },
 useFilters,
)
Enter fullscreen mode Exit fullscreen mode

现在,我们要在表格中添加列筛选器的用户界面。如果该列应用了筛选器,我们将通过调用render()筛选字段的列方法来渲染筛选器的用户界面。否则,不执行任何操作。

<th {...column.getHeaderProps()}>
 {column.render('Header')}
 <div>{column.canFilter ? column.render('Filter') : null}</div>
</th>
Enter fullscreen mode Exit fullscreen mode

等等!我们还没定义筛选器的用户界面。我们需要一个函数来实现这个功能——对于我们的筛选函数,首先我们需要知道还剩下多少行数据需要筛选,以便将该数字显示为一个输入框占位符。然后,我们会渲染一个输入框,<input>供用户输入他们想要筛选的内容。

function TextFilter({
 column: { filterValue, preFilteredRows, setFilter },
}) {
 const count = preFilteredRows.length
 return (
   <input
     value={filterValue || ''}
     onChange={e => {
       setFilter(e.target.value || undefined)
     }}
     placeholder={`Search ${count} records...`}
   />
 )
}
Enter fullscreen mode Exit fullscreen mode

我们的函数从TextFilter()中接收三个值

  • filterValue = 此当前用于筛选的 值。
    • 该值由表的状态筛选器对象设置。
  • preFilteredRows = 在进行任何筛选之前传递给列的行数组。
  • setFilter是一个函数,它接受一个filterValue 参数,用于更新此列的 filterValue(在本例中,它接受用户输入的值)。

    <input>

    )。

定义好筛选函数后,我们将更新列对象定义,添加一个Filter字段。将以下代码添加到 Table 函数中:

const defaultColumn = React.useMemo(
 () => ({
   Filter: TextFilter,
 }),
 []
)
Enter fullscreen mode Exit fullscreen mode

最后,使用时请确保传入defaultColumncolumns 和 data 参数useTable()

const {
 getTableProps,
 ...
} = useTable(
 {
   columns,
   data,
   defaultColumn,
 },
 useFilters,
)
Enter fullscreen mode Exit fullscreen mode

Retool-React-Table-Filtering

分页

本教程示例中创建的数据量与实际应用中的数据量相比非常小。只有六行?拜托!实际上,我们的客服代表需要处理成百上千行的客户数据。为了避免长时间的渲染和需要滚动浏览的大量页面,我们将为表格添加分页功能。这样,react-table 每次只需渲染部分行,从而减轻客服代表查看海量数据的压力。

分页功能是通过使用usePagination()react-table 的 Hook 实现的。请务必将其添加到您的导入语句中:

import { useTable, usePagination } from 'react-table' 
Enter fullscreen mode Exit fullscreen mode

接下来,你需要将参数传递usePagination()useTable()Hook,设置初始状态(如果你想从pageIndex0 以外的任何值开始,或者大于pageSize或小于 10),并从其返回值中提取额外的属性。

const {
   getTableProps,
   headerGroups,
   getRowProps,
   prepareRow,
   page,
   pageOptions,
   state: { pageIndex, pageSize },
   previousPage,
   nextPage,
   canPreviousPage,
   canNextPage,
 } = useTable(
   {
     columns,
     data,
     initialState: { pageSize: 2 },
   },
   usePagination,
 )
Enter fullscreen mode Exit fullscreen mode

请注意,除了像之前分页前那样遍历` rowsin`之外<tbody>,我们还要遍历page`,它类似于 `,但rows只包含页面可容纳的行数。如果不这样做,无论您点击多少次按钮,数据都不会移动。相信我。

<tbody {...getTableBodyProps()}>
 {page.map(row => {
   prepareRow(row)
   ...
Enter fullscreen mode Exit fullscreen mode

在这个例子中,我们有一个按钮可以跳转到上一页,一个按钮可以跳转到下一页,还有一个输入框可以让用户输入要跳转到的页码。

return (
   <div>
     <table {...getTableProps()}>
       ...
     </table>
     <div>
       <button onClick={() => previousPage()} disabled={!canPreviousPage}>
         Previous Page
       </button>
       <button onClick={() => nextPage()} disabled={!canNextPage}>
         Next Page
       </button>
       <div>
         Page{' '}
         <em>
           {pageIndex + 1} of {pageOptions.length}
         </em>
       </div>
     </div>
   </div>
 )
}
Enter fullscreen mode Exit fullscreen mode

Retool-React-Table-Pagination

请查看此代码沙箱,了解使用 react-table 实现分页的更高级版本。

使用 react-table 构建的表格组件

希望本教程能帮助您了解如何使用 react-table 在 React 中创建、设置样式和扩展表格。如需更高级的教程,我们推荐您查看 react-table 文档中的“ Kitchen Sink ”示例。该示例包含了 react-table 大部分功能的完整示例,涵盖分页、排序、筛选、分组、展开行和行选择等。

文章来源:https://dev.to/retool/building-a-react-table-component-with-react-table-4d48