- 前端
- UI组件
【免费下载链接】table
🤖 Headless UI for building powerful tables & datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table
本篇技术指南聚焦 TanStack Table(table-core 核心 + React/Vue/Solid/Svelte 等框架适配层)中列可见性(Column Visibility)特性的两个表级配置选项——enableHiding与onColumnVisibilityChange。它们定义于TableOptions_ColumnVisibility接口,是启用列隐藏/显示能力、接入受控状态(controlled state)的入口。读完本文,你将掌握这两个选项的默认行为、与columnVisibility状态的关系、外部 Atom 与回调两种状态管理模式,并能结合源码与示例搭建一个完整可用的"显示/隐藏列"控制面板。
接口概览:TableOptions_ColumnVisibility
该接口在仓库中的定义位于 columnVisibilityFeature.types.ts,完整签名如下:
export interface TableOptions_ColumnVisibility { /** * Whether to enable column hiding. Defaults to `true`. */ enableHiding?: boolean /** * Called with an updater when column visibility state changes. Pair this with * `state.columnVisibility` when using external state; external atoms can own * the slice without this callback. */ onColumnVisibilityChange?: OnChangeFn<ColumnVisibilityState> }接口仅包含两个可选属性,集中体现了列可见性特性的全部表级配置面:
| 属性 | 类型 | 默认值 | 作用 |
|---|---|---|---|
enableHiding | boolean(可选) | true | 是否允许隐藏列(表级总开关) |
onColumnVisibilityChange | OnChangeFn<ColumnVisibilityState>(可选) | 由makeStateUpdater生成的内部更新器 | 列可见性状态变化时被调用的回调,配合state.columnVisibility实现受控模式 |
配套的状态类型ColumnVisibilityState定义在同一文件顶部(columnVisibilityFeature.types.ts):
export type ColumnVisibilityState = Record<string, boolean>即一个"列 ID → 布尔值"的映射:某列 ID 的值为false表示该列被隐藏;值为true或该列 ID 不在映射中都表示该列可见。这一"缺失即可见"的语义是整个特性的核心约定,下文源码分析会反复印证。
选项一:enableHiding——控制列能否被隐藏
enableHiding是列隐藏能力的总开关,默认值为true,即默认情况下所有列都可以被隐藏或重新显示。当你希望某些列(例如主键、操作按钮列)始终固定可见时,有两种层级可以关闭隐藏能力。
表级全局禁用
将enableHiding: false传入useTable的选项对象,即可让所有列都无法隐藏:
const table = useTable({ features, columns, data, enableHiding: false, // 全局禁止隐藏任何列 })列级定向禁用
更常见的做法是在**列定义(ColumnDef)**上使用列级enableHiding,只锁定特定列。列级选项定义于 ColumnDef_ColumnVisibility(源码见 columnVisibilityFeature.types.ts):
const columns = [ { header: 'ID', accessorKey: 'id', enableHiding: false, // 该列禁止隐藏 }, { header: 'Name', accessorKey: 'name', // 未设置,默认可隐藏 }, ]组合判定逻辑:column_getCanHide
列级与表级两个开关并非二选一,而是**"与"关系**。核心实现位于 columnVisibilityFeature.utils.ts:
export function column_getCanHide(column) { return ( (column.columnDef.enableHiding ?? true) && (column.table.options.enableHiding ?? true) ) }即:只有当列定义的enableHiding(缺省视为true)且表级enableHiding(缺省视为true)都为真时,该列才允许被隐藏。任一设置为false,column.getCanHide()都会返回false。测试用例 columnVisibilityFeature.utils.test.ts 分别验证了"全局禁用"与"列级禁用"两条路径,均使column_getCanHide返回false。
提示:即使某列被锁定为不可隐藏,它的 ID 仍可能出现在
columnVisibility状态映射中,但column_toggleVisibility在调用前会先检查column_getCanHide,不可隐藏的列会被直接跳过,状态不会被修改(见 utils 实现 columnVisibilityFeature.utils.ts)。
选项二:onColumnVisibilityChange——受控状态的更新回调
onColumnVisibilityChange的完整类型为OnChangeFn<ColumnVisibilityState>,即"接收一个更新器(updater)的函数"。更新器可以是新的状态映射,也可以是接收旧状态并返回新状态的函数:
// 直接传入新映射 onColumnVisibilityChange({ visits: false }) // 或传入函数式更新器 onColumnVisibilityChange((old) => ({ ...old, visits: false }))该回调在以下场景被触发:
table.setColumnVisibility(updater)table.toggleAllColumnsVisible(value)- 任一列执行
column.toggleVisibility(value) table.resetColumnVisibility(defaultState)
默认行为:内部状态更新器
即使你不传该选项,列可见性依然可用。feature 在注册时通过getDefaultTableOptions为onColumnVisibilityChange提供了默认实现,见 columnVisibilityFeature.ts:
getDefaultTableOptions: (table) => { return { onColumnVisibilityChange: makeStateUpdater('columnVisibility', table), } }makeStateUpdater('columnVisibility', table)生成一个内部更新器,负责将新状态写回表的state.columnVisibility并触发相关订阅。这解释了为什么"什么都不配"也能开箱即用地隐藏/显示列——默认情况下表格自己管理这份状态。
受控模式:state.columnVisibility + onColumnVisibilityChange
当你需要自行拥有columnVisibility状态(例如持久化用户偏好、与表单联动、在表格外部读写),v8 风格的受控模式仍然受支持:把状态值传给state.columnVisibility,把 setter 传给onColumnVisibilityChange:
const [columnVisibility, setColumnVisibility] = useState<ColumnVisibilityState>({ columnId1: true, columnId2: false, // 默认隐藏该列 columnId3: true, }) const table = useTable({ features, columns, data, state: { columnVisibility, }, onColumnVisibilityChange: setColumnVisibility, })推荐方案:外部 Atom(无需该回调)
按原文档的说明(React 指南),外部 Atom 可以直接拥有这个状态切片,从而无需onColumnVisibilityChange回调。外部 Atom 提供全应用范围内的细粒度订阅,表格之外的其他代码也能读写可见性状态,而无需重新渲染拥有表格的组件:
import { useCreateAtom, useSelector } from '@tanstack/react-store' import { columnVisibilityFeature, tableFeatures, useTable } from '@tanstack/react-table' import type { ColumnVisibilityState } from '@tanstack/react-table' const features = tableFeatures({ columnVisibilityFeature }) const columnVisibilityAtom = useCreateAtom<ColumnVisibilityState>({ columnId1: true, columnId2: false, // 默认隐藏该列 columnId3: true, }) const columnVisibility = useSelector(columnVisibilityAtom) // 在任意需要的地方订阅 const table = useTable({ features, columns, data, atoms: { columnVisibility: columnVisibilityAtom, }, })此时可见性状态的写入经由atoms.columnVisibility直接落到外部 Atom,onColumnVisibilityChange不再是必需项。这是 v9 框架适配层推荐的状态管理方式。
非受控模式:initialState 设定初始值
如果状态完全交给表格内部管理,只需用initialState.columnVisibility指定初始可见性:
const table = useTable({ features, columns, data, initialState: { columnVisibility: { columnId1: true, columnId2: false, // 首屏默认隐藏该列 columnId3: true, }, }, })注意:若
columnVisibility同时出现在initialState和state中,state的初始化优先,initialState会被忽略。二者只能二选一(见 React 指南 的 NOTE 提示)。
状态语义与默认状态
列可见性 feature 在注册时通过getInitialState注入初始状态(columnVisibilityFeature.ts),默认状态由getDefaultColumnVisibilityState()生成,即一个空对象{}(实现见 columnVisibilityFeature.utils.ts)。
空对象的语义是:所有列 ID 在映射中缺失,因此所有列默认可见。测试 columnVisibilityFeature.utils.test.ts 验证了getDefaultColumnVisibilityState()返回{},且column_getIsVisible在默认情况下返回true。
column_getIsVisible的判定逻辑(columnVisibilityFeature.utils.ts)还处理了两种特殊情况:
- 叶子列(leaf):读取
state.columnVisibility[column.id],缺失时回退为true; - 父/分组列(parent/group):自身没有直接状态条目,而是递归判断任一子列可见则该父列可见。测试用例"should return true if any child column is visible"验证了这一点(columnVisibilityFeature.utils.test.ts)。
配套 API 家族:Table、Column、Row 三层
TableOptions_ColumnVisibility只是配置入口,特性启用后会为三个对象注入完整 API(类型定义见 columnVisibilityFeature.types.ts,注册逻辑见 columnVisibilityFeature.ts)。
Table 级 API
| API | 说明 |
|---|---|
getIsAllColumnsVisible() | 是否所有叶子列都可见(用于"全选"复选框的 checked) |
getIsSomeColumnsVisible() | 是否至少一个叶子列可见(用于三态控制) |
getToggleAllColumnsVisibilityHandler() | 复选框风格处理器,读取event.target.checked并切换全部列 |
getVisibleFlatColumns() | 当前可见的扁平列列表(含仍有可见后代的父列) |
getVisibleLeafColumns() | 当前可见的叶子列列表(行单元格与表头渲染通常用它) |
resetColumnVisibility(defaultState?) | 重置为initialState.columnVisibility;传true则忽略初始状态、重置为{} |
setColumnVisibility(updater) | 以新映射或更新器函数更新可见性状态 |
toggleAllColumnsVisible(value?) | 显示/隐藏所有可隐藏的叶子列 |
getToggleAllColumnsVisibilityHandler的源码(columnVisibilityFeature.utils.ts)展示了它与onColumnVisibilityChange的联动:处理器把event.target.checked传给table_toggleAllColumnsVisible,后者构建完整映射并调用table_setColumnVisibility,最终经由setStateSlice路由到onColumnVisibilityChange(默认是内部更新器,受控模式下是你传入的 setter)。
Column 级 API
| API | 说明 |
|---|---|
getCanHide() | 该列是否允许被隐藏(综合列级与表级enableHiding) |
getIsVisible() | 该列当前是否可见 |
getToggleVisibilityHandler() | 复选框风格处理器,读取event.target.checked切换该列 |
toggleVisibility(value?) | 切换该列可见性;不传值时自动取反 |
完整签名见 Column_ColumnVisibility。
Row 级 API
| API | 说明 |
|---|---|
getVisibleCells() | 该行中属于可见列的单元格(启用列固定时按 start → center → end 排序) |
getVisibleCellsByColumnId() | 以列 ID 为键的可见单元格映射,隐藏列被剔除 |
渲染要点:务必使用"可见"系列 API
启用列隐藏后,渲染表头、表体与表脚时不要使用table.getAllLeafColumns()、row.getAllCells()这类忽略可见性的 API,而要改用table.getVisibleLeafColumns()、row.getVisibleCells(),否则被隐藏的列仍会渲染出来。表头分组 API(table.getHeaderGroups()等)本身已经考虑了列可见性,可放心使用(见 React 指南)。
table_getVisibleLeafColumns与row_getVisibleCells的实现(columnVisibilityFeature.utils.ts)都是先取全集、再按column_getIsVisible过滤;row_getVisibleCells额外处理了列固定(pinning)下的排序。两个 API 在 columnVisibilityFeature.ts 中注册了 memo 依赖(含columnVisibility、columnOrder、columnPinning、grouping等状态),状态变化时自动失效重算。
端到端示例:构建"显示/隐藏列"控制面板
仓库的官方示例 examples/react/column-visibility/src/main.tsx 是上述选项与 API 的完整落地,核心结构如下:
const features = tableFeatures({ columnVisibilityFeature }) const table = useTable({ features, columns, data, // initialState: { columnVisibility: { visits: false } }, // 首屏隐藏某列 // atoms: { columnVisibility: columnVisibilityAtom }, // 推荐:外部 Atom 拥有状态 // state: { columnVisibility }, // 受控模式 // onColumnVisibilityChange: setColumnVisibility, // enableHiding: false, // 全局禁止隐藏 debugTable: true, }) // 全选开关 + 逐列开关 <table> <thead> {table.getHeaderGroups().map((headerGroup) => ( <tr key={headerGroup.id}> {headerGroup.headers.map((header) => ( <th key={header.id} colSpan={header.colSpan}> {header.isPlaceholder ? null : <table.FlexRender header={header} />} </th> ))} </tr> ))} </thead> <tbody> {table.getRowModel().rows.map((row) => ( <tr key={row.id}> {row.getVisibleCells().map((cell) => ( <td key={cell.id}> <table.FlexRender cell={cell} /> </td> ))} </tr> ))} </tbody> </table>控制面板部分将"全选"复选框绑定到table.getIsAllColumnsVisible()与table.getToggleAllColumnsVisibilityHandler(),将逐列复选框绑定到column.getIsVisible()与column.getToggleVisibilityHandler(),与 React 指南 中给出的模板一致。该示例同时提供了 e2e 冒烟测试 examples/react/column-visibility/tests/e2e/smoke.spec.ts,用于验证切换列可见性后的渲染结果。
提示:可见性菜单通常渲染列本身而非表头对象——被隐藏的列可能没有活动的表头上下文。建议使用稳定的文本标签(如自定义的"列 ID → 标签"映射、
columnDef.meta中的label字段,或直接使用column.id)作为开关文案(见 React 指南 的 NOTE)。
源码链路小结
将以上内容串起来,一次"隐藏某列"操作背后的完整调用链为:
- UI 触发
column.getToggleVisibilityHandler()或column.toggleVisibility(false); column_toggleVisibility校验column_getCanHide(列级与表级enableHiding与运算),并将新的可见性写入各叶子列(columnVisibilityFeature.utils.ts);table_setColumnVisibility通过setStateSlice将更新器路由到onColumnVisibilityChange(columnVisibilityFeature.utils.ts);- 默认情况下该回调是
makeStateUpdater生成的内部更新器,将状态写回表内;受控模式下则由你的onColumnVisibilityChange接收更新器并驱动state.columnVisibility;外部 Atom 模式下状态直接落到 Atom; - 状态变化使
getVisibleLeafColumns、getVisibleCells等 memo 依赖失效,重算后触发渲染,隐藏的列从表头与表体中消失。
常见误区
- 使用全集 API 渲染:
getAllLeafColumns()/getAllCells()不感知可见性,渲染时必须改用getVisibleLeafColumns()/getVisibleCells(); initialState与state同时提供columnVisibility:state优先,initialState被忽略,请二选一;- 误以为
enableHiding: false会从状态中清除该列:不可隐藏的列只是不会被toggleVisibility修改,若它已在状态映射中,仍可通过setColumnVisibility显式赋值(虽然不推荐); - 将分组列 ID 直接写入状态:可见性状态按叶子列 ID 索引,对分组列调用
toggleVisibility会扩散到其可隐藏的叶子列,而非写入分组列自身 ID(有测试用例专门验证,见 columnVisibilityFeature.utils.test.ts)。
延伸阅读
- React 框架列可见性指南(各框架均有对应指南,如 Vue、Angular、Svelte、Solid)
- Column_ColumnVisibility 接口 与 ColumnDef_ColumnVisibility 接口
- Table_ColumnVisibility 接口 与 TableState_ColumnVisibility 接口
- columnVisibilityFeature 变量文档 及各静态函数文档(如 table_setColumnVisibility、column_toggleVisibility)
- 官方示例 examples/react/column-visibility(其他框架如 Vue、Svelte 也有对应版本)
- 前端
- UI组件
【免费下载链接】table
🤖 Headless UI for building powerful tables & datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table
相关推荐
TanStack Octane Table 列显隐(Column Visibility)特性实战指南
TanStack Octane Table 列显隐(Column Visibility)特性实战指南 本篇指南聚焦 TanStack Octane Table
前端UI组件TanStack Table v9 的 Lit 列可见性(Column Visibility)实战指南:隐藏/显示列的完整实现方案
TanStack Table v9 的 Lit 列可见性(Column Visibility)实战指南:隐藏/显示列的完整实现方案 导读 本文基于 TanSta
前端UI组件TanStack Svelte Table v9 列可见性(Column Visibility)完全指南:状态管理、切换 API 与渲染实践
TanStack Svelte Table v9 列可见性(Column Visibility)完全指南:状态管理、切换 API 与渲染实践 本文聚焦 TanS
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考