在复杂后台系统中,表格展示的字段往往不是固定的。用户角色不同、业务模块不同,所需要的列也不一样。React Data Grid 类组件如果采用硬编码列配置,后期每增加一个字段都要改组件代码,既容易出错也不利于协作。动态列的思路是把列的定义变成可配置的数据,由后端或前端逻辑驱动渲染。

一、什么是动态列以及为什么需要它
动态列指的是表格的列结构不是在写组件时写死的,而是根据运行时的数据字段或用户配置来生成。比如一个订单列表,普通员工只能看订单号和金额,主管还能看客户信息和利润率。如果写死列,就要写两套表格组件;用动态列,只需传入不同的字段元信息。
从维护角度看,动态列把界面描述和数据描述分离。后端返回字段数组,前端将其映射为列定义对象,再交给网格组件渲染。这样新增字段时,前端组件本身不需要改动,只调整数据接口即可。对于使用 React Data Grid、ag-Grid React 或官方 @tanstack/react-table 的项目,这种模式都能显著降低耦合。
二、基础实现:用状态管理列定义
最核心的一步是把列配置放到 React 的 state 中。通常我们会用 useState 保存 columns,用另一个 state 保存 rowData。当接口返回新的字段元信息时,通过 setColumns 更新即可触发重渲染。
下面以一个简单的自定义 React Data Grid 为例,展示如何从接口字段生成列。我们假设后端返回的格式是 [{ field: 'name', label: '姓名' }, ...]。
import React, { useState, useEffect } from 'react';
// 模拟接口返回的字段元信息
const mockSchema = [
{ field: 'id', label: '编号' },
{ field: 'name', label: '姓名' },
{ field: 'age', label: '年龄' }
];
// 模拟行数据
const mockRows = [
{ id: 1, name: '张三', age: 28 },
{ id: 2, name: '李四', age: 32 }
];
export default function DynamicGrid() {
const [columns, setColumns] = useState([]);
const [rows, setRows] = useState([]);
useEffect(() => {
// 根据 schema 生成列定义
const cols = mockSchema.map(item => ({
key: item.field,
title: item.label
}));
setColumns(cols);
setRows(mockRows);
}, []);
return (
<table border="1" cellPadding="8">
<thead>
<tr>
{columns.map(col => (
<th key={col.key}>{col.title}</th>
))}
</tr>
</thead>
<tbody>
{rows.map((row, idx) => (
<tr key={idx}>
{columns.map(col => (
<td key={col.key}>{row[col.key]}</td>
))}
</tr>
))}
</tbody>
</table>
);
}
上面的代码把列定义存在 columns 状态里,渲染时遍历 columns 输出表头和单元格。如果 mockSchema 换成其他字段,表格结构会自动变化,不需要改 JSX 结构。
这种写法的优点是直观、依赖少。缺点是没有利用专业网格组件的能力,比如排序、虚拟化。但在理解动态列原理上已经足够。实际项目中可以把它替换成 ag-Grid 的 columnDefs 或 TanStack Table 的 columns 配置。
三、结合 ag-Grid React 实现动态列
ag-Grid 是目前流行的 React Data Grid 方案,它用 columnDefs 数组描述列。动态列就是动态修改这个数组。我们可以在组件内监听字段 schema,生成对应的 columnDefs 并 setState。
下面的例子演示根据权限字段过滤列。假设后端返回可用字段列表,我们据此生成 columnDefs,隐藏无权限字段。
import React, { useState, useEffect } from 'react';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
const allFields = [
{ field: 'orderNo', headerName: '订单号' },
{ field: 'amount', headerName: '金额' },
{ field: 'customer', headerName: '客户' },
{ field: 'profit', headerName: '利润率' }
];
export default function AgDynamicGrid({ allowedFields }) {
const [columnDefs, setColumnDefs] = useState([]);
const [rowData] = useState([
{ orderNo: 'A001', amount: 100, customer: '公司甲', profit: 0.2 },
{ orderNo: 'A002', amount: 200, customer: '公司乙', profit: 0.3 }
]);
useEffect(() => {
// 仅保留允许的字段
const defs = allFields.filter(f => allowedFields.includes(f.field));
setColumnDefs(defs);
}, [allowedFields]);
return (
<div className="ag-theme-alpine" style={{ height: 300, width: 600 }}>
<AgGridReact columnDefs={columnDefs} rowData={rowData} />
</div>
);
}
当父组件传入不同的 allowedFields,比如 ['orderNo', 'amount'],表格就只显示两列。这比写多个网格组件灵活得多,也方便和后端权限系统打通。
需要注意,ag-Grid 在 columnDefs 引用变化时会重新初始化列。如果数据量大,频繁改列可能造成闪烁。可以用 useMemo 缓存列定义,或在必要时才更新 state,避免无谓渲染。
四、动态列的常见坑与优化
第一个坑是 key 不稳定。生成列时如果没有给稳定的 key(例如用索引当 key),在列顺序变化时 React 会错误复用 DOM,导致显示错乱。务必用字段名做 key。
第二个坑是过度渲染。有些开发者把 rowData 和 columns 放在同一个 useEffect 里请求,任何一列变化都重拉全部数据。更好的做法是列 schema 和行数据分开接口,列变更只改 columnDefs,不重取数据。
| 做法 | 优点 | 缺点 |
|---|---|---|
| 硬编码列 | 简单直接,性能好 | 不灵活,维护成本高 |
| 全动态列 | 适应多场景,扩展强 | 需处理 key 与渲染性能 |
| 半动态列(固定+扩展) | 平衡稳定与灵活 | 扩展列能力受限 |
如果项目列结构大部分固定,只有少数字段变化,可以采用半动态列:核心列写死,扩展列由配置生成。这样既能减少重复代码,又避免全盘动态带来的复杂度。
五、总结
在 React Data Grid 中创建动态列,本质是把列定义数据化、状态化。无论是自写表格还是使用 ag-Grid、TanStack Table,只要抓住 columns 来自 state、随 schema 更新这个核心,就能轻松应对多变业务。实践中注意 key 稳定和渲染边界,动态列就会成为提升开发效率的利器。
React_Data_Griddynamic_columnscolumn_definition修改时间:2026-08-12 02:57:42