驾驭数据洪流:Material React Table 入门指南
在现代 Web 应用开发中,数据展示是不可或缺的一环。特别是需要呈现大量结构化数据时,一个功能强大、界面友好的表格组件就显得尤为重要。React 生态中有众多优秀的表格库,而 Material React Table (MRT) 凭借其基于 TanStack Table(原 React Table v8)的强大“无头”(headless)能力与 Material UI 的优雅设计风格相结合,成为了构建企业级数据表格的热门选择。
本文将带你深入了解 Material React Table,从零开始,手把手教你如何在你的 React 项目中引入并使用它,逐步解锁其核心功能,最终驾驭数据洪流,构建出兼具美观与功能的交互式数据表格。
引言:为何选择 Material React Table?
在构建 React 应用时,我们经常需要处理和展示各种类型的数据,其中表格是最常见的表现形式之一。简单的表格可以用 HTML 的 <table>
标签结合 JSX 来实现,但这通常意味着你需要手动处理诸如排序、分页、过滤、编辑等复杂交互逻辑。随着数据量的增长和功能需求的增加,手动维护这样的表格会变得异常困难且容易出错。
这时,一个成熟的表格库就成为了必需品。Material React Table 正是为了解决这一痛点而生。它不是从头构建一个表格,而是巧妙地结合了两个强大的生态系统:
- TanStack Table (v8+): 这是一个“无头”的表格库。所谓“无头”,是指它只负责核心的数据处理逻辑(如排序、过滤、分组、分页、虚拟化等),但不提供任何 UI 渲染。这使得它极具灵活性,你可以用任何 UI 库或自定义样式来渲染它处理后的数据。
- Material UI (MUI v5+): 这是 Google 开发的一个流行的 React UI 库,实现了 Material Design 设计语言。它提供了大量高质量的、可定制的 UI 组件,包括表格相关的组件(
Table
,TableHead
,TableBody
,TableRow
,TableCell
等)。
Material React Table 的核心思想就是:利用 TanStack Table 的强大逻辑能力,结合 Material UI 的优秀组件和样式,为你提供一个开箱即用、功能丰富且外观符合 Material Design 规范的 React 表格组件。
选择 Material React Table 的优势显而易见:
- 功能丰富: 它继承了 TanStack Table 的大部分核心功能,包括但不限于:分页、排序、全局搜索、列过滤、列隐藏、列重排、列缩放、行选择、行展开、数据编辑(多种模式)、数据分组、固定列/表头等等。几乎所有你能想到的表格高级功能,MRT 都提供了支持。
- Material Design 风格: 无缝集成 Material UI 组件,使得表格外观符合 Material Design 规范,与使用 Material UI 构建的其他应用部分风格统一,无需额外大量样式定制。
- 易于使用: MRT 将复杂的 TanStack Table 配置封装在一个易于使用的 React 组件中,通过简洁的 Props 即可启用和配置各种功能。
- 灵活性: 虽然提供了默认的 Material UI 渲染,但它也保留了 TanStack Table 的部分灵活性,允许你通过渲染函数高度定制单元格、表头、甚至整行或整个表格的显示。
- 活跃的社区和良好的文档: TanStack Table 和 Material UI 都有庞大的用户基础和活跃的社区,MRT 本身也有详细的文档和丰富的示例。
如果你正在使用 Material UI 构建应用,并且需要一个功能强大且美观的表格来展示数据,那么 Material React Table 绝对是一个值得考虑的优秀选择。
准备工作:环境要求
在开始使用 Material React Table 之前,请确保你的开发环境满足以下要求:
- Node.js 和 npm/yarn/pnpm: 你需要安装 Node.js 环境,并使用 npm、yarn 或 pnpm 作为包管理器。
- React: 你的项目需要是基于 React 构建的,并且推荐使用较新的 React 版本(例如 React 17 或 18)。
- Material UI (v5 或更高版本): Material React Table 依赖于 Material UI 组件。你需要确保你的项目中已经安装并设置好了 Material UI v5 或更高版本。这通常包括
@mui/material
、@mui/icons-material
以及样式引擎依赖(@emotion/react
和@emotion/styled
或styled-components
)。
如果你还没有安装 Material UI,可以通过以下命令安装:
“`bash
npm install @mui/material @emotion/react @emotion/styled @mui/icons-material
或者
yarn add @mui/material @emotion/react @emotion/styled @mui/icons-material
或者
pnpm add @mui/material @emotion/react @emotion/styled @mui/icons-material
“`
注意: @mui/icons-material
不是 MRT 的硬性依赖,但它在很多内置功能(如图标按钮)中非常有用,推荐一起安装。样式引擎 (@emotion/*
或 styled-components
) 是 Material UI v5+ 所必需的。
第一步:安装 Material React Table
安装 Material React Table 非常简单,只需要使用你的包管理器运行以下命令:
“`bash
npm install material-react-table
或者
yarn add material-react-table
或者
pnpm add material-react-table
“`
安装完成后,你就可以在你的 React 组件中引入 MaterialReactTable
组件了。
第二步:基本用法 – 渲染你的第一个表格
安装完成后,我们来创建一个最简单的表格。一个基本的表格至少需要两个核心配置项:数据 (data) 和 列定义 (columns)。
假设我们有一些用户数据要展示:
javascript
const data = [
{
firstName: '迪迦',
lastName: '奥特曼',
age: 3000000,
address: 'M78 星云',
state: '光之国',
},
{
firstName: '赛罗',
lastName: '奥特曼',
age: 20000,
address: '宇宙警备队',
state: '光之国',
},
{
firstName: '泽塔',
lastName: '奥特曼',
age: 5000,
address: '光之国',
state: '光之国',
},
// ...更多数据
];
接下来,我们需要定义表格的列。列定义告诉 MRT 如何从数据对象中提取对应列的值,以及列的表头显示文本。列定义是一个对象数组,每个对象代表一列。最基本的列定义需要 header
(表头文本)和 accessorKey
(数据对象中的属性名)。
“`javascript
import React, { useMemo } from ‘react’;
import { MaterialReactTable } from ‘material-react-table’;
// 数据 (与上面示例相同)
const data = [/ … /];
const MyBasicTable = () => {
// 定义列。使用 useMemo 可以优化性能,特别是当列定义复杂时
const columns = useMemo(
() => [
{
accessorKey: ‘firstName’, // 数据中对应的 key
header: ‘名’, // 表头显示文本
},
{
accessorKey: ‘lastName’,
header: ‘姓’,
},
{
accessorKey: ‘age’,
header: ‘年龄’,
},
{
accessorKey: ‘address’,
header: ‘地址’,
},
{
accessorKey: ‘state’,
header: ‘状态’,
},
],
[] // 依赖项为空数组,确保列定义只创建一次
);
return (
);
};
export default MyBasicTable;
“`
将 MyBasicTable
组件添加到你的应用中,你就能看到一个基于 Material UI 风格的基础表格了。
第三步:核心概念详解
在使用 Material React Table 时,理解其核心概念至关重要,特别是 data
和 columns
的配置。
数据 (data)
data
Prop 必须是一个对象数组。MRT 会遍历这个数组,数组中的每个对象代表表格的一行。每个对象的属性将与你在 columns
定义中指定的 accessorKey
或通过 accessorFn
提取的值相对应。
数据对象的结构可以比较灵活,甚至包含嵌套对象或数组,这可以通过 accessorFn
来处理。
列定义 (columns)
columns
Prop 是一个对象数组,用于定义表格的每一列及其行为和显示。数组中的每个对象至少需要包含:
header
:string
– 列的表头文本。accessorKey
:string
– 如果列的数据直接对应数据对象的一个属性,使用此属性指定键名。例如,accessorKey: 'name'
将显示rowData.name
的值。accessorFn
:(originalRow: TData) => any
– 如果列的数据需要经过计算、格式化,或者对应嵌套的数据,可以使用此函数。函数接收整行数据对象作为参数,并返回该列需要显示的值。如果你使用accessorFn
,通常也需要为该列指定一个唯一的id
,因为 MRT 使用id
来跟踪列的状态(如排序、过滤)。
accessorKey
vs accessorFn
示例:
假设数据如下:
javascript
const complexData = [
{
id: 1,
person: {
firstName: '史蒂夫',
lastName: '罗杰斯',
},
address: {
city: '纽约',
country: '美国',
},
},
// ...
];
使用 accessorKey
访问嵌套数据:
javascript
{
accessorKey: 'person.firstName', // 使用点号访问嵌套属性
header: '名',
}
使用 accessorFn
处理更复杂的逻辑或访问嵌套数据:
javascript
{
id: 'fullName', // 使用 accessorFn 时推荐设置 id
header: '全名',
accessorFn: (row) => `${row.person.firstName} ${row.person.lastName}`, // 组合名和姓
},
{
id: 'location',
header: '位置',
accessorFn: (row) => `${row.address.city}, ${row.address.country}`, // 组合城市和国家
},
{
accessorKey: 'id', // 如果id是顶层属性,可以直接用 accessorKey
header: 'ID',
}
accessorKey
通常更简洁高效,适合直接访问顶层或简单的嵌套属性。accessorFn
提供了更大的灵活性,适合处理复杂的数据提取或转换逻辑。
其他重要的列配置属性:
列定义对象还有许多其他属性用于控制列的行为和显示:
id
:string
– 列的唯一标识符。如果使用accessorFn
或需要程序化地控制列状态,强烈建议提供。如果使用accessorKey
,MRT 会自动将其作为id
。Cell
:(cell: MRT_Cell<TData>, row: MRT_Row<TData>, table: MRT_TableInstance<TData>) => ReactNode
– 用于自定义渲染单元格内容的函数。这是高度定制单元格显示的关键。你可以返回 JSX 元素,从而在单元格中渲染按钮、图片、图标、格式化文本等。Header
:() => ReactNode
– 用于自定义渲染列头内容的函数。Footer
:() => ReactNode
– 用于自定义渲染列脚内容的函数。filterVariant
:MRT_FilterOption
– 指定该列使用的过滤输入框类型(如 ‘text’, ‘select’, ‘range’ 等)。enableSorting
:boolean
– 是否启用该列的排序(默认为 true,除非列定义中没有可排序的值)。enableFiltering
:boolean
– 是否启用该列的过滤输入框(默认为 true,除非列定义中没有可过滤的值)。enableColumnActions
:boolean
– 是否在该列表头显示列操作菜单(排序、过滤、隐藏等)。默认为 true。size
:number
– 列的宽度提示(以像素为单位)。MRT 会尝试根据这个提示分配宽度,但最终宽度受内容、其他列、以及列宽缩放功能影响。muiTableHeadCellProps
,muiTableBodyCellProps
,muiTableFooterCellProps
:mui.TableCellProps
– 允许你直接向 Material UI 的TableCell
组件传递 Props,以定制单个列的表头、表体、表脚单元格样式或行为。
第四步:启用常用功能
Material React Table 提供了丰富的交互功能,并且通过简单的 Props 即可启用。以下是一些最常用的功能及其启用方法:
1. 分页 (Pagination)
启用分页功能可以有效处理大量数据,避免一次性加载所有数据导致的性能问题。
javascript
<MaterialReactTable
columns={columns}
data={data}
enablePagination // 启用分页功能
// 可选的初始化状态配置
initialState={{
pagination: {
pageIndex: 0, // 初始页码 (从0开始)
pageSize: 10, // 每页显示条数
},
}}
/>
启用 enablePagination
后,MRT 会在表格底部显示分页控件,包括页码、每页条数选择、上一页/下一页按钮。
你可以通过 initialState.pagination
设置初始的页码和每页条数。
对于服务器端分页,你需要控制分页状态并手动获取数据。这涉及到 rowCount
, onPaginationChange
等 Prop,我们会在后面介绍。
2. 排序 (Sorting)
排序允许用户按照某一列的值对表格数据进行升序或降序排列。
javascript
<MaterialReactTable
columns={columns}
data={data}
enableSorting // 启用排序功能 (所有可排序的列)
// 可选:启用多列排序
enableMultiSort
// 可选的初始化状态配置
initialState={{
sorting: [{ id: 'lastName', desc: false }], // 初始按姓氏升序排列
}}
/>
enableSorting
默认对所有列启用排序。你也可以在单个列定义中通过 enableSorting: false
禁用特定列的排序。
enableMultiSort
允许用户按住 Shift 键点击多个列头进行多列排序。
initialState.sorting
是一个对象数组,用于设置初始的排序状态。每个对象包含 id
(列的 ID)和 desc
(是否降序,boolean)。
对于服务器端排序,你需要控制排序状态并手动获取数据。这涉及到 onSortingChange
等 Prop。
3. 过滤 (Filtering)
过滤功能允许用户根据特定条件筛选表格中显示的数据。MRT 提供了全局过滤和列过滤两种方式。
全局过滤 (Global Filter)
一个搜索框,用户输入内容后,MRT 会搜索所有列中包含该内容的数据行。
javascript
<MaterialReactTable
columns={columns}
data={data}
enableGlobalFilter // 启用全局过滤搜索框
// 可选的初始化状态配置
initialState={{ globalFilter: '奥特曼' }} // 初始全局过滤值为 '奥特曼'
/>
启用 enableGlobalFilter
后,MRT 会在表格顶部显示一个全局搜索输入框。
列过滤 (Column Filtering)
在每一列的表头下方显示一个过滤输入框,用户可以针对该列进行过滤。
javascript
<MaterialReactTable
columns={columns}
data={data}
enableFiltering // 启用列过滤功能 (所有可过滤的列)
// 可选的初始化状态配置
initialState={{
columnFilters: [{ id: 'age', value: '>10000' }], // 初始按年龄过滤 (大于 10000)
}}
/>
enableFiltering
默认对所有列启用列过滤。你也可以在单个列定义中通过 enableFiltering: false
禁用特定列的过滤。
MRT 会根据列的数据类型(如果推断出来)或你设置的 filterVariant
来渲染不同类型的过滤输入框(文本、数字范围、日期范围、选择框等)。
initialState.columnFilters
是一个对象数组,用于设置初始的列过滤状态。每个对象包含 id
(列的 ID)和 value
(过滤值)。过滤值的格式取决于 filterVariant
和过滤操作符。
对于服务器端过滤,你需要控制过滤状态并手动获取数据。这涉及到 onColumnFiltersChange
, onGlobalFilterChange
等 Prop。
4. 行选择 (Row Selection)
允许用户选择表格中的一行或多行。通常用于批量操作。
“`javascript
import { MaterialReactTable, MRT_RowSelectionState } from ‘material-react-table’;
import React, { useMemo, useState } from ‘react’;
// … columns and data
const MySelectableTable = () => {
const [rowSelection, setRowSelection] = useState
// … columns definition
return (
);
};
“`
enableRowSelection
会在每一行的开头添加一个复选框,并在表头添加一个“全选”复选框。
state.rowSelection
是一个对象,键是行的 ID(默认是索引,如果你的数据有唯一ID,推荐在 getRowId
Prop 中指定),值是布尔值表示该行是否被选中。
onRowSelectionChange
是一个回调函数,当用户改变行选择状态时触发,接收最新的 rowSelection
状态作为参数。你需要使用 useState
或其他状态管理工具来存储并更新这个状态,实现受控的行选择。
5. 列可见性控制 (Column Hiding)
允许用户动态隐藏或显示表格的列。MRT 提供了内置的列控制菜单。
javascript
<MaterialReactTable
columns={columns}
data={data}
enableHiding // 启用列隐藏菜单
// 可选的初始化状态配置
initialState={{
columnVisibility: { address: false, state: false }, // 初始隐藏地址和状态列
}}
/>
enableHiding
会在表格右上角添加一个按钮,点击后会弹出一个菜单,用户可以在其中勾选或取消勾选来控制列的显示与隐藏。
initialState.columnVisibility
是一个对象,键是列的 ID,值是布尔值(true
表示显示,false
表示隐藏)。
第五步:定制化表格外观和行为
Material React Table 提供了多种方式来定制表格的样式和行为,以满足你的具体需求。
1. 定制单元格渲染 (Cell Rendering)
这是最常用的定制方式之一。通过在列定义中提供 Cell
函数,你可以完全控制该列每个单元格的渲染内容。
Cell
函数接收一个包含 cell
, row
, table
等信息的对象作为参数:
cell
: TanStack Table 的cell
对象,包含该单元格的各种状态和数据。cell.getValue()
可以获取该单元格的原始值。row
: TanStack Table 的row
对象,代表当前行的数据和状态。row.original
可以获取原始数据行对象。table
: TanStack Table 的table
实例,可以访问整个表格的状态和方法。
javascript
// 在 columns 定义中
{
accessorKey: 'age',
header: '年龄',
Cell: ({ cell }) => (
<span style={{ fontWeight: 'bold', color: cell.getValue() > 100000 ? 'red' : 'inherit' }}>
{cell.getValue()} 年
</span>
),
},
{
accessorKey: 'state',
header: '状态',
Cell: ({ cell }) => {
const state = cell.getValue();
const color = state === '光之国' ? 'primary' : 'secondary';
return (
<Chip label={state} color={color} variant="outlined" size="small" />
);
},
},
{
accessorKey: 'firstName',
header: '头像',
Cell: ({ row }) => (
<Avatar src={`/avatars/${row.original.firstName}.png`} alt={row.original.firstName}>
{row.original.firstName[0]}
</Avatar>
),
}
上面的例子展示了如何在单元格中:
* 根据值设置文本样式(加粗、颜色)。
* 渲染 Material UI 的 Chip 组件。
* 渲染 Material UI 的 Avatar 组件,并使用数据中的其他属性(如 firstName)作为图片来源和备用文本。
2. 定制表头/表脚渲染 (Header/Footer Rendering)
类似地,你可以使用列定义中的 Header
和 Footer
函数来定制列头和列脚的内容。
javascript
{
accessorKey: 'age',
header: '年龄',
Header: () => (
<Typography variant="h6" color="primary">
年纪
</Typography>
),
Footer: () => (
<Typography variant="caption">
年龄合计 (未实现)
</Typography>
),
// ... 其他列属性和 Cell 属性
}
3. 定制表格、行、单元格的 Material UI Props
Material React Table 提供了大量的 mui*Props
Prop,允许你直接向内部使用的 Material UI 组件传递 Props,从而灵活地定制样式或行为。
muiTableContainerProps
: 传递给最外层TableContainer
组件的 Props。muiTableProps
: 传递给Table
组件的 Props。muiTableHeadProps
: 传递给TableHead
组件的 Props。muiTableBodyProps
: 传递给TableBody
组件的 Props。muiTableFooterProps
: 传递给TableFooter
组件的 Props。muiTableHeadRowProps
: 传递给表头TableRow
组件的 Props。muiTableBodyRowProps
: 传递给表体TableRow
组件的 Props。muiTableFooterRowProps
: 传递给表脚TableRow
组件的 Props。muiTableHeadCellProps
: 传递给所有表头TableCell
组件的 Props。muiTableBodyCellProps
: 传递给所有表体TableCell
组件的 Props。muiTableFooterCellProps
: 传递给所有表脚TableCell
组件的 Props。
重要: 除了作用于所有对应元素的 mui*Props
外,你还可以在列定义中为特定列设置 muiTableHeadCellProps
、muiTableBodyCellProps
、muiTableFooterCellProps
。在列定义中设置的 Props 会覆盖顶层同名 Props 的同名属性(如果存在冲突)。
示例:改变行 hover 效果和特定列单元格样式
javascript
<MaterialReactTable
columns={columns}
data={data}
muiTableBodyRowProps={({ row }) => ({ // 作用于所有行
sx: {
cursor: 'pointer', // 鼠标悬停时显示指针
backgroundColor: row.getIsSelected() ? 'lightblue' : 'inherit', // 如果被选中,背景色变蓝
'&:hover': { // 鼠标悬停时的样式
backgroundColor: '#f5f5f5',
},
},
onClick: (event) => console.log('Row clicked', row.original), // 添加行点击事件
})}
muiTableBodyCellProps={({ column }) => ({ // 作用于所有表体单元格
sx: {
fontWeight: column.id === 'firstName' ? 'bold' : 'normal', // 如果是firstName列,文本加粗
},
})}
// ... 其他 Props
/>
通过这些 mui*Props
,你可以利用 Material UI 的样式系统 (sx Prop 或 styled
API) 或直接传递其他 Props 来精细控制表格各个部分的样式和行为。
4. 定制整个表格容器
你可以使用 muiTableContainerProps
来定制包裹整个表格的容器样式,例如设置最大高度、添加滚动条等。
javascript
<MaterialReactTable
columns={columns}
data={data}
muiTableContainerProps={{
sx: { maxHeight: '500px', overflowY: 'auto' }, // 设置最大高度并添加垂直滚动条
}}
// ...
/>
第六步:更高级的功能
Material React Table 提供了许多更高级的功能,这里简要介绍几个常用或强大的:
1. 数据编辑 (Data Editing)
MRT 支持多种数据编辑模式:行内编辑、模态框编辑、全屏编辑等。通过配置 editingMode
和相应的保存/取消回调函数来启用。
“`javascript
import { MaterialReactTable, MRT_Row, MRT_TableOptions } from ‘material-react-table’;
import React, { useMemo, useState, useCallback } from ‘react’;
// … columns and initialData
const MyEditableTable = () => {
const [data, setData] = useState(initialData); // 数据需要在组件内部可变
// … columns definition
// 保存编辑行的回调
const handleSaveRowEdits: MRT_TableOptions
useCallback(
async ({ exitEditingMode, row, values }) => {
// 假设 values 是编辑后的数据对象
// row.original 是原始数据对象
// 在这里执行保存逻辑,比如发送API请求
console.log(‘保存编辑:’, values);
// 模拟更新本地状态
data[row.index] = values;
setData([…data]); // 更新状态触发重新渲染
exitEditingMode(); // 退出编辑模式
},
[data] // 依赖 data 状态
);
// 取消编辑的回调
const handleCancelRowEdits = useCallback(
() => {
console.log(‘取消编辑’);
// 通常不需要做什么,因为没有修改数据
},
[]
);
return (
//
// )}
// onCreatingRowSave={handleSaveNewRow} // 保存新创建行时触发
// onCreatingRowCancel={handleCancelNewRow} // 取消创建新行时触发
/>
);
};
“`
你需要将数据存储在组件的状态中 (useState
),并提供 onEditingRowSave
和 onEditingRowCancel
(或其他模式对应的回调) 函数来处理保存和取消逻辑。编辑模式 (editingMode
) 可以设置为 modal
(模态框), row
(行内编辑), cell
(单元格编辑), table
(整个表格可编辑)。
对于创建新行,可以使用 renderTopToolbarCustomActions
渲染一个按钮,点击后调用 table.setCreatingRow(true)
,并实现 onCreatingRowSave
和 onCreatingRowCancel
回调。
2. 行操作 (Row Actions)
在每一行的末尾添加一个列,用于放置按钮或其他操作(如编辑、删除、查看详情)。
“`javascript
import { MaterialReactTable, MRT_Row } from ‘material-react-table’;
import { Box, IconButton, Tooltip } from ‘@mui/material’;
import EditIcon from ‘@mui/icons-material/Edit’;
import DeleteIcon from ‘@mui/icons-material/Delete’;
import React, { useMemo } from ‘react’;
// … columns and data
const MyTableWithActions = () => {
// … columns definition
const handleDeleteRow = (row: MRT_Row
if (confirm(确定删除 ${row.original.firstName} ${row.original.lastName} 吗?
)) {
// 在这里执行删除逻辑,例如发送API请求
console.log(‘删除行:’, row.original);
// 更新本地状态 (如果数据在本地管理)
// setData(data.filter(item => item.id !== row.original.id));
}
};
return (
)}
// 如果你使用了 enableEditing=”row”,上面的编辑按钮可以直接调用 table.setEditingRow(row)
// 如果你在模态框或其他地方处理编辑,onClick 中执行相应的打开模态框逻辑
/>
);
};
“`
enableRowActions
会在表格的最后一列(或者第一列,取决于 positionActionsColumn
Prop)添加一个额外的列。
renderRowActions
是一个函数,接收 row
和 table
对象,返回用于渲染该列内容的 React 节点。你可以在这里放置按钮、图标等。
3. 固定列和表头 (Column Pinning / Sticky Header)
对于宽表格,固定重要的列(如第一列的名称或操作列)和表头可以显著提升用户体验。
javascript
<MaterialReactTable
columns={columns}
data={data}
enableColumnPinning // 启用列固定功能
enableStickyHeader // 启用固定表头
// 可选的初始化状态配置
initialState={{
columnPinning: { left: ['firstName', 'lastName'], right: ['mrt-row-actions'] }, // 固定姓名列在左侧,操作列在右侧 (mrt-row-actions 是默认的操作列ID)
}}
muiTableContainerProps={{ sx: { maxHeight: '500px' } }} // 固定表头通常需要设置容器最大高度并有滚动条
/>
enableColumnPinning
允许用户(或通过 initialState
配置)将列固定在表格的左侧或右侧。默认操作列的 ID 是 mrt-row-actions
,你可以将它固定在右侧。
enableStickyHeader
使表头在表格垂直滚动时保持可见。使用此功能通常需要将表格容器的高度限制住(例如使用 muiTableContainerProps
设置 maxHeight
和 overflowY: 'auto'
)。
4. 服务器端数据处理
对于非常大的数据集,通常需要在服务器端进行分页、排序、过滤和全局搜索,而不是一次性将所有数据加载到客户端。
实现服务器端数据处理的关键在于:
- 将 MRT 的相关状态(分页、排序、过滤、全局搜索)存储在你的组件状态中。
- 监听 MRT 的状态变化事件 (
onPaginationChange
,onSortingChange
,onColumnFiltersChange
,onGlobalFilterChange
)。 - 在这些状态变化事件发生时,根据最新的状态向你的后端 API 发起数据请求。
- 后端 API 响应请求,返回当前页的数据、总条数等信息。
- 更新组件的状态,将获取到的新数据和总条数 (
rowCount
) 传递给 MRT。 - 设置
manualPagination
,manualSorting
,manualFiltering
,manualGlobalFilter
Props 为true
,告诉 MRT 不要自己处理这些逻辑,而是等待你提供处理后的数据。
这是一个简化的概念示例:
“`javascript
import { MaterialReactTable, MRT_PaginationState, MRT_SortingState, MRT_ColumnFiltersState } from ‘material-react-table’;
import React, { useMemo, useState, useEffect, useCallback } from ‘react’;
// 假设这是一个异步获取数据的函数
const fetchUsers = async ({ pageIndex, pageSize, sorting, columnFilters, globalFilter }) => {
// 构建API请求参数
const params = {
page: pageIndex + 1, // API通常从1开始计数
limit: pageSize,
sortBy: sorting.map(s => ${s.id}:${s.desc ? 'desc' : 'asc'}
).join(‘,’),
filters: columnFilters.map(f => ${f.id}:${f.value}
).join(‘,’),
globalFilter: globalFilter,
};
console.log(“Fetching data with params:”, params);
// 模拟网络延迟
await new Promise(resolve => setTimeout(resolve, 500));
// 模拟API响应数据
const allData = [/ 你的所有模拟数据 /];
let filteredData = […allData];
// 模拟服务器端过滤
if (globalFilter) {
filteredData = filteredData.filter(row =>
Object.values(row).some(value =>
String(value).toLowerCase().includes(globalFilter.toLowerCase())
)
);
}
if (columnFilters && columnFilters.length > 0) {
columnFilters.forEach(filter => {
filteredData = filteredData.filter(row =>
String(row[filter.id]).toLowerCase().includes(String(filter.value).toLowerCase())
);
});
}
const sortedData = […filteredData].sort((a, b) => {
// 模拟服务器端排序 (简化版)
if (sorting.length === 0) return 0;
const sort = sorting[0];
const valueA = a[sort.id];
const valueB = b[sort.id];
if (valueA < valueB) return sort.desc ? 1 : -1;
if (valueA > valueB) return sort.desc ? -1 : 1;
return 0;
});
const start = pageIndex * pageSize;
const end = start + pageSize;
const paginatedData = sortedData.slice(start, end);
return {
data: paginatedData,
meta: {
totalRowCount: filteredData.length, // 注意:服务器端应返回过滤和排序后的总数
},
};
};
const MyServerSideTable = () => {
const [data, setData] = useState([]);
const [isFetching, setIsFetching] = useState(false); // 添加加载状态
const [rowCount, setRowCount] = useState(0); // 总条数
// 表格状态
const [pagination, setPagination] = useState
const [sorting, setSorting] = useState
const [columnFilters, setColumnFilters] = useState
const [globalFilter, setGlobalFilter] = useState
// 定义列 (与之前相同)
const columns = useMemo(/ … /, []);
// 异步获取数据的 effect
useEffect(() => {
const getData = async () => {
setIsFetching(true);
const result = await fetchUsers({
pageIndex: pagination.pageIndex,
pageSize: pagination.pageSize,
sorting,
columnFilters,
globalFilter,
});
setData(result.data);
setRowCount(result.meta.totalRowCount);
setIsFetching(false);
};
getData();
}, [pagination, sorting, columnFilters, globalFilter]); // 依赖表格状态变化
return (
);
};
“`
在服务器端模式下,你需要完全控制表格的状态,并在状态变化时自行获取数据。MRT 此时只负责根据你提供的数据和状态进行渲染,并触发相应的事件回调。rowCount
Prop 告诉 MRT 数据总共有多少条,以便正确显示分页信息。
第七步:与 Material UI ThemeProvider 配合
由于 Material React Table 是基于 Material UI 构建的,它会继承你在应用根部通过 ThemeProvider
设置的 Material UI 主题。这意味着你可以通过修改主题来统一表格的颜色、字体、间距等样式,而无需单独为表格编写大量 CSS。
确保你的应用根部有 ThemeProvider
:
“`javascript
import { createTheme, ThemeProvider } from ‘@mui/material/styles’;
import CssBaseline from ‘@mui/material/CssBaseline’;
import App from ‘./App’; // 你的应用根组件
const theme = createTheme({
// 在这里定义你的主题
palette: {
primary: {
main: ‘#556cd6’,
},
secondary: {
main: ‘#19857b’,
},
},
// … 其他主题定制
});
ReactDOM.createRoot(document.getElementById(‘root’)).render(
);
“`
MRT 的组件和样式会遵循这个主题,例如按钮颜色、选中行的背景色、Loading 指示器的颜色等。
故障排除与使用技巧
useMemo
的重要性: 对于columns
和data
,尤其是当它们是计算属性或在父组件状态变化时可能重新创建时,强烈建议使用useMemo
进行包裹。这可以防止不必要的重复计算和 MRT 内部的重复渲染,显著提升性能。- 唯一 ID: 如果你的数据对象有唯一的 ID 属性(如
id
或_id
),请在 MRT 组件上设置getRowId={(row) => row.id}
。这有助于 MRT 在数据变化时正确跟踪行,对于编辑、选择等功能非常重要。 - 查看文档和示例: Material React Table 的官方文档和示例非常丰富和详细。当你需要实现特定功能或遇到问题时,查阅官方资源是最有效的途径。
- 检查控制台警告: MRT 和 TanStack Table 会在控制台输出有用的警告信息,提示你配置可能存在的问题或潜在的性能瓶颈。
- Material UI 依赖: 确保正确安装了 Material UI v5+ 及其样式引擎依赖,否则 MRT 可能无法正常渲染或出现样式问题。
结语
Material React Table 是一个功能强大、高度可定制且与 Material UI 生态系统紧密结合的 React 表格组件。通过本文的介绍,你已经了解了如何安装并开始使用 MRT,配置基本的数据和列,启用常用的交互功能,并掌握了如何定制表格的外观和行为,甚至了解了服务器端数据处理的基本思路。
从简单的静态数据展示,到复杂的交互式表格,再到与后端 API 集成处理大规模数据,Material React Table 都能提供可靠的支持。随着你对它越来越熟悉,你会发现它能极大地简化你在 React 应用中构建复杂数据表格的工作。
现在,开始在你的项目中尝试 Material React Table 吧!查阅官方文档,探索更多高级功能,根据你的具体需求进行定制,相信它会成为你构建出色用户界面的有力工具。