news 2026/9/21 16:07:14

TanStack Table 列可见性(Column Visibility)表级选项深度解析:enableHiding 与 onColumnVisibilityChange 完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TanStack Table 列可见性(Column Visibility)表级选项深度解析:enableHiding 与 onColumnVisibilityChange 完整指南
  • 前端
  • UI组件

【免费下载链接】table

🤖 Headless UI for building powerful tables & datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table

项目地址:https://gitcode.com/gh_mirrors/ta/table
点击查看免费下载

本篇技术指南聚焦 TanStack Table(table-core 核心 + React/Vue/Solid/Svelte 等框架适配层)中列可见性(Column Visibility)特性的两个表级配置选项——enableHidingonColumnVisibilityChange。它们定义于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> }

接口仅包含两个可选属性,集中体现了列可见性特性的全部表级配置面:

属性类型默认值作用
enableHidingboolean(可选)true是否允许隐藏列(表级总开关)
onColumnVisibilityChangeOnChangeFn<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)都为真时,该列才允许被隐藏。任一设置为falsecolumn.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 在注册时通过getDefaultTableOptionsonColumnVisibilityChange提供了默认实现,见 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同时出现在initialStatestate中,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_getVisibleLeafColumnsrow_getVisibleCells的实现(columnVisibilityFeature.utils.ts)都是先取全集、再按column_getIsVisible过滤;row_getVisibleCells额外处理了列固定(pinning)下的排序。两个 API 在 columnVisibilityFeature.ts 中注册了 memo 依赖(含columnVisibilitycolumnOrdercolumnPinninggrouping等状态),状态变化时自动失效重算。

端到端示例:构建"显示/隐藏列"控制面板

仓库的官方示例 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)。

源码链路小结

将以上内容串起来,一次"隐藏某列"操作背后的完整调用链为:

  1. UI 触发column.getToggleVisibilityHandler()column.toggleVisibility(false)
  2. column_toggleVisibility校验column_getCanHide(列级与表级enableHiding与运算),并将新的可见性写入各叶子列(columnVisibilityFeature.utils.ts);
  3. table_setColumnVisibility通过setStateSlice将更新器路由到onColumnVisibilityChange(columnVisibilityFeature.utils.ts);
  4. 默认情况下该回调是makeStateUpdater生成的内部更新器,将状态写回表内;受控模式下则由你的onColumnVisibilityChange接收更新器并驱动state.columnVisibility;外部 Atom 模式下状态直接落到 Atom;
  5. 状态变化使getVisibleLeafColumnsgetVisibleCells等 memo 依赖失效,重算后触发渲染,隐藏的列从表头与表体中消失。

常见误区

  • 使用全集 API 渲染getAllLeafColumns()/getAllCells()不感知可见性,渲染时必须改用getVisibleLeafColumns()/getVisibleCells()
  • initialStatestate同时提供columnVisibilitystate优先,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

项目地址:https://gitcode.com/gh_mirrors/ta/table
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/21 16:05:44

Matlab在综合能源系统优化调度与容量配置中的应用

1. 项目背景与核心价值综合能源系统作为能源互联网的重要载体&#xff0c;正在重塑传统能源生产与消费模式。这个Matlab项目聚焦于解决一个关键痛点&#xff1a;如何在源&#xff08;风电、光伏等可再生能源&#xff09;与荷&#xff08;电力负荷&#xff09;双重不确定性条件下…

作者头像 李华
网站建设 2026/9/21 16:01:58

React开发者转投Hyperapp:6大概念映射与心智模型迁移完全指南

React开发者转投Hyperapp&#xff1a;6大概念映射与心智模型迁移完全指南 【免费下载链接】hyperapp 1kB-ish JavaScript framework for building hypertext applications 项目地址: https://gitcode.com/gh_mirrors/hy/hyperapp 对于 React 开发者来说&#xff0c;学习…

作者头像 李华
网站建设 2026/9/21 15:54:14

多层复合吸波体设计与工程实践

1. 项目背景与核心价值在电磁兼容和隐身技术领域&#xff0c;频率选择吸波体&#xff08;FSS&#xff09;一直是工程实践中的关键材料。传统单层吸波体往往只能在窄带范围内实现有效吸收&#xff0c;而现代电子设备的工作频段越来越宽&#xff0c;这就催生了对多层复合结构的研…

作者头像 李华