TanStack Table (Octane) 行选择(Row Selection)完整指南:从状态管理到 Shift 范围选择
前端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点击查看免费下载导读行选择Row Selection是数据表格最常用的交互能力之一。本文以 TanStack Table v9 的 Octane 适配版tanstack/octane-table为核心系统讲解如何通过rowSelectionFeature启用行选择、读写选择状态、用外部 Atom 管理状态、配置条件选择与单选/子行选择并深入剖析 Shift 范围选择Shift Range Selection的交互细节。读完本文你将掌握在 Octane 应用中实现带复选框、全选、单选与范围选择的完整方案并理解其背后的源码实现与测试验证。快速上手启用行选择行选择功能由rowSelectionFeature提供。将它传入tableFeatures()组合进 feature 集合再交给useTable行选择相关的全部 API 即自动生效。import { useTable, tableFeatures, rowSelectionFeature, } from tanstack/octane-table const features tableFeatures({ rowSelectionFeature }) const table useTable({ features, columns, data, })从源码看rowSelectionFeature是一个标准的TableFeature对象它负责四件事见 rowSelectionFeature.ts初始化状态getInitialState为表格注册rowSelection状态切片默认值为{}空对象提供默认选项getDefaultTableOptions一次性注册enableRowSelection、enableMultiRowSelection、enableSubRowSelection、enableRowRangeSelection以及isRowRangeSelectionEvent五个选项的默认值扩展 Row 原型向row实例挂载getIsSelected、getCanSelect、getToggleSelectedHandler、toggleSelected等 API扩展 Table 实例向table实例挂载getSelectedRowModel、toggleAllRowsSelected、getIsAllRowsSelected等 API。这也解释了文档中Adding the row selection feature enables the related APIs的含义所有行选择 API 都是该 feature 在实例构造阶段统一挂载的。需要说明的是octane-table包本身对table-core是逐字转出verbatim re-export行选择的全部实现位于核心包 rowSelectionFeature.ts 及其工具函数 rowSelectionFeature.utils.ts。仓库提供了完整的可运行示例examples/octane/row-selection该示例同时叠加了分页、列过滤与全局过滤并在 main.tsrx 中演示了多 feature 的组合方式。读取行选择状态表格实例会自动托管rowSelection状态。Octane 版v9提供了四类读取 APIAPI作用table.state.rowSelection响应式读取行选择状态由useTable的 selector 订阅getSelectedRowModel()返回所有被选中的行getFilteredSelectedRowModel()过滤后仍被选中的行getGroupedSelectedRowModel()分组、排序后仍被选中的行console.log(table.state.rowSelection) // 行选择状态 - { 1: true, 2: false, ... } console.log(table.getSelectedRowModel().rows) // 全部客户端选中行 console.log(table.getFilteredSelectedRowModel().rows) // 过滤后的选中行 console.log(table.getGroupedSelectedRowModel().rows) // 分组后的选中行在事件回调等非渲染代码中还可以用table.atoms.rowSelection.get()读取当前快照。这种读取不会订阅组件到未来的变更因此在渲染位置请优先使用table.state.rowSelection或table.Subscribe。在源码层面这三个 RowModel API 都带有memoDeps缓存依赖见 rowSelectionFeature.tsgetSelectedRowModel依赖table.atoms.rowSelection与getCoreRowModel()getFilteredSelectedRowModel依赖选择状态与getFilteredRowModel()getGroupedSelectedRowModel依赖选择状态与getSortedRowModel()。也就是说选中状态变化或对应 RowModel 变化时缓存会自动失效并重算这正是过滤后选中行等语义能保持准确的底层机制。[!NOTE] 若使用了manualPagination请留意getSelectedRowModel只能基于传入的data生成行因此只会返回当前页的选中行但rowSelection状态本身可以安全地保存不在data中的行 id。管理行选择状态推荐外部 Atomv9 方式如果需要在应用其他部分方便地读取选中行 id例如用于发起 API 调用推荐通过atoms表选项把rowSelection状态切片交给外部 Atom 持有。Atom 能保留细粒度订阅应用任意位置都能读取选择值而不会迫使拥有表格的组件整体重渲染。import { useCreateAtom, useSelector } from tanstack/octane-store import { useTable, tableFeatures, rowSelectionFeature, type RowSelectionState, } from tanstack/octane-table const features tableFeatures({ rowSelectionFeature }) const rowSelectionAtom useCreateAtomRowSelectionState({}) // 在任意需要的地方订阅该 Atom const rowSelection useSelector(rowSelectionAtom) const table useTable({ features, // ... atoms: { rowSelection: rowSelectionAtom, // 选择 API 将更新该 Atom }, })关于atoms的优先级与行为tanstack/octane-table的 table-state 技能说明 明确外部 Atom 是直接同步所有者优先级高于options.state表格 API 的写入会直接到达该 Atom。同时建议外部 Atom 与on[State]Change回调不要成对使用二者是两种互斥的所有权模式。更完整的对比见 Table State Guide。兼容受控 state onRowSelectionChangev8 风格v8 风格的state.rowSelectiononRowSelectionChange模式仍然受支持。它对简单集成或迁移 v8 代码较为方便但细粒度不如外部 Atom。const [rowSelection, setRowSelection] useStateRowSelectionState({}) const table useTable({ features, // ... onRowSelectionChange: setRowSelection, state: { rowSelection, }, })示例项目 main.tsrx 同时注释展示了两种可切换的写法默认使用rowSelectionAtom并保留了state.rowSelectiononRowSelectionChange以及initialState.rowSelection首屏预选的备选方案方便对照。使用更有意义的行 id行选择状态以row id 为键。默认情况下每个行的 row id 就是row.index即 0、1、2…这在数据排序、过滤或刷新后会变得不可靠。使用行选择功能时几乎总是应该通过getRowId指定一个稳定的唯一标识const table useTable({ features, // ... getRowId: (row) row.uuid, // 用数据库中的 uuid 作为 row id })设置后选择状态形如{ 13e79140-62a8-4f9c-b087-5da737903b76: true, f3e2a5c0-5b7a-4d8a-9a5c-9c9b8a8e5f7e: false }而不是这样{ 0: true, 1: false }示例中即采用getRowId: (row) row.id数据由 makeData.ts 生成id 为 uuid。e2e 测试 smoke.spec.ts 也专门注释Row ids are uuids, so only the number of selected keys is assertable佐证了行 id 是 uuid 这一事实。条件启用与禁用行选择行选择默认对所有行启用。enableRowSelection选项接受布尔值或行级函数可精确控制const table useTable({ // ... enableRowSelection: (row) row.original.age 18, // 仅允许成年人行被选中 })源码实现见 rowSelectionFeature.utils.ts非常直接export function row_getCanSelect(row) { const options row.table.options if (typeof options.enableRowSelection function) { return options.enableRowSelection(row) } return options.enableRowSelection ?? true }即传入函数时逐行求值否则返回布尔默认值true。UI 侧请用row.getCanSelect()决定复选框是否禁用disabled属性与源码中的判断保持一致。单选模式默认允许多选。设置enableMultiRowSelection为false可强制单选适合用单选按钮代替复选框的场景也可以传函数对某行的子行条件禁用多选const table useTable({ // ... enableMultiRowSelection: false, // 同一时刻仅允许一行被选中 // enableMultiRowSelection: row row.original.age 18, // 对成年人行启用单选 })子行Sub-Row选择默认情况下选中父行会连带选中其全部子行。enableSubRowSelection可禁用该行为const table useTable({ // ... enableSubRowSelection: false, // 禁用子行联动选择 // enableSubRowSelection: row row.original.age 18, // 对成年人行禁用子行选择 })子行选择对全选 API 同样生效当父行屏蔽子行选择时table.toggleAllRowsSelected()与table.toggleAllPageRowsSelected()会跳过该父行的后代getIsAllRowsSelected()与getIsAllPageRowsSelected()在判定是否全选时也会忽略这些后代。选中父行会把父行 id 与其所有可选的子行 id一起写入选择状态。默认情况下之后取消子行不会移除父行 id——因为部分表格把状态中的 id 视为字面选中项。如果需要可向 toggle API 传入deselectParents选项在取消选中时清理祖先 idrow.getToggleSelectedHandler({ deselectParents: true }) // 或 row.toggleSelected(false, { deselectParents: true })源码中row_toggleSelected的处理逻辑见 rowSelectionFeature.utils.ts印证了这一行为写入时按(opts?.selectChildren ?? true) row_getCanMultiSelect(row)决定是否递归选中后代当!value opts?.deselectParents为真时调用pruneAncestorRowIds清除祖先 id。Shift 范围选择row.getToggleSelectedHandler()默认支持 Shift 范围选择一次普通的可选中行交互会建立锚点anchor随后按住 Shift 点击另一行会选中/取消选中两者之间的闭区间区间内所有行的选中状态由被点击复选框的结果值决定被点击的端点成为下一次 Shift 交互的锚点。事件识别逻辑在 feature 默认选项中见 rowSelectionFeature.ts只要事件暴露event.shiftKey或event.nativeEvent.shiftKey即判定为范围选择。这也是官方建议Octane 复选框处理器用onClick而不是onChange的原因——onClick才能把带shiftKey修饰键的点击事件完整传给处理器。可以整体禁用范围选择或替换事件判定例如改用平台修饰键 Meta/Ctrlconst table useTable({ // ... enableRowRangeSelection: false, // 例如用平台修饰键代替 Shift // isRowRangeSelectionEvent: event // Boolean((event as { metaKey?: boolean }).metaKey), })范围选择遵循表格当前的逻辑显示顺序包括过滤、分组、排序与展开。客户端分页下范围可以跨页因为使用分页前的完整顺序手动/服务端分页下只有当前data中已加载的行能参与范围。当子行选择启用时范围内遇到的父行默认会递归切换其可选择的子行。若只想让显示顺序区间中明确存在的行变化传入selectChildren: falseconst handler row.getToggleSelectedHandler({ selectChildren: false, })交互锚点的生命周期规则值得注意只要锚点行 id 仍在显示顺序中它会跨排序、过滤、分组、展开、分页与数据更新保持若过滤或数据替换移除了锚点下一次 Shift 交互退化为普通行切换并建立新锚点resetRowSelection、任一全选 API、table.reset()都会清除锚点直接调用row.toggleSelected()、table.setRowSelection()或外部受控状态变更不会建立或移动锚点。源码中锚点由表格实例数据_lastSelectedRowId保存feature 在initTableInstanceData与resetTableInstanceData中将其初始化为null见 rowSelectionFeature.ts与reset 会清除锚点的文档描述完全对应。e2e 测试 smoke.spec.ts 验证了范围选择的闭区间语义选中第 1 行后Shift 点击第 5 行期望选择数为 5两端点都包含且区间外的第 0、6 行不被选中。渲染行选择 UITanStack Table 不规定 UI 形态——复选框、单选按钮或整行点击均可。表格实例提供了一系列 API 辅助渲染。连接复选框输入以下 handler 可直接绑定到复选框输入它们会自动调用内部 API 更新状态并触发重渲染row.getToggleSelectedHandler()切换单行选中table.getToggleAllRowsSelectedHandler()切换全部行跨页table.getToggleAllPageRowsSelectedHandler()切换当前页全部行。若需要更细粒度的控制也可以直接使用row.toggleSelected()、table.toggleAllRowsSelected()或像操作任何状态更新器一样调用table.setRowSelection()。这些 handler 本质上只是便捷封装。const columns [ { id: select-col, header: ({ table }) ( Checkbox checked{table.getIsAllRowsSelected()} indeterminate{table.getIsSomeRowsSelected()} onChange{table.getToggleAllRowsSelectedHandler()} // 或 getToggleAllPageRowsSelectedHandler / ), cell: ({ row }) ( Checkbox checked{ row.getIsSelected() || (row.getCanSelectSubRows() row.getIsAllSubRowsSelected()) } disabled{!row.getCanSelect()} indeterminate{row.getIsSomeSelected()} onClick{row.getToggleSelectedHandler()} / ), }, // ...更多列定义... ][!NOTE]getCanSelectSubRows()与getIsAllSubRowsSelected()子句只对含子行的表格有意义扁平数据下仅row.getIsSelected()就足够。完整的子行模式包括用deselectParents清理废弃父 id可参考 expanding 示例。连接整行点击若想要更简洁的交互把点击事件直接绑到tr上即可row.getToggleSelectedHandler()同样适用tbody {table.getRowModel().rows.map((row) { return ( tr key{row.id} className{row.getIsSelected() ? selected : null} onClick{row.getToggleSelectedHandler()} {row.getVisibleCells().map((cell) { return td key{cell.id}{/* */}/td })} /tr ) })} /tbody半选indeterminate状态行选择的部分选中通过getIsSomeRowsSelected/getIsSomePageRowsSelected/row.getIsSomeSelected表达。示例项目 main.tsrx 中的IndeterminateCheckbox组件演示了完整做法在useEffect中根据indeterminate布尔值设置原生ref.current.indeterminate。e2e 测试也对半选状态做了断言见 smoke.spec.ts选中 1000 行中的 1 行时表头与表尾全选框均为indeterminatetrue且未勾选。与分页、过滤的联动语义示例与测试共同验证了行选择与分页、过滤联动时的重要语义表头全选框绑定getIsAllRowsSelected()跨所有页测试点击后期望选中 1000 行翻页后新页面行依然处于选中状态因为选择以行 id 为键见 smoke.spec.ts表尾全选框绑定getIsAllPageRowsSelected()仅当前页测试中期望选中 10 行且表头保持半选选择汇总Object.keys(table.state.rowSelection).length统计所有被选中行 id 的数量其分母使用getPreFilteredRowModel()因此全局过滤不会改变分母见 smoke.spec.ts。小结在 TanStack Table v9Octane 版中行选择由rowSelectionFeature一个开关式启用随后即可获得状态读取、条件选择、单选、子行联动、Shift 范围选择与全套 UI handler。选择状态的托管推荐使用外部 Atom 以获得细粒度订阅行 id 务必通过getRowId指定为业务唯一键子行场景注意selectChildren与deselectParents两个选项的配合。上述所有行为均有核心源码rowSelectionFeature.ts、rowSelectionFeature.utils.ts、可运行示例examples/octane/row-selection与 Playwright e2e 测试smoke.spec.ts三重印证可直接作为落地实现的参照。赞分享前端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 Preact 行选择Row Selection完整指南从状态管理到 Shift 区间多选TanStack Table Preact 行选择Row Selection完整指南从状态管理到 Shift 区间多选 本文以 TanStack Tabl前端UI组件Puppeteer ElementHandle.type() 输入方法详解聚焦元素的模拟键盘输入机制与延迟控制Puppeteer ElementHandle.type 输入方法详解聚焦元素的模拟键盘输入机制与延迟控制 ElementHandle.type 是 Pupp前端UI组件Angular TanStack Table 行选择Row Selection完整指南从状态管理到多选、范围选择与父子行联动Angular TanStack Table 行选择Row Selection完整指南从状态管理到多选、范围选择与父子行联动 导读 行选择Row Sel前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Netdiscover实战指南:ARP扫描在局域网资产发现中的应用

Netdiscover实战指南:ARP扫描在局域网资产发现中的应用

简介:Netdiscover 是一款面向网络管理员、安全审计人员和无线网络运维工程师的开源地址扫描工具,专注解决无 DHCP 无线网络中设备发现与信息收集难题,通过主动发送 ARP 请求快速定位在线设备,并输出 IP 地址、MAC 地址、网络掩码等…

2026/9/21 0:49:28 阅读更多 →
Hugging Face 开源权重模型怎么接到 TaoToken:Kimi K2.7 Code 的 OpenRouter 用量观察

Hugging Face 开源权重模型怎么接到 TaoToken:Kimi K2.7 Code 的 OpenRouter 用量观察

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/21 0:48:28 阅读更多 →
样本量要多大才够 —— 别让 “样本太少“ 成为硬伤

样本量要多大才够 —— 别让 “样本太少“ 成为硬伤

做问卷调查,最常被问到的问题就是:"你发了多少份问卷?" 样本量太小,统计结果不可靠;样本量太大,收集起来费劲。到底多少样本量才合适?汇写(https://www.huixielunwen.com/…

2026/9/22 1:04:34 阅读更多 →

最新新闻

STM32实战:SPI从机主动发送数据的3种实现方案

STM32实战:SPI从机主动发送数据的3种实现方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/22 2:57:45 阅读更多 →
Linux设备驱动模型深度解析:从device到probe再到sysfs

Linux设备驱动模型深度解析:从device到probe再到sysfs

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/22 2:57:45 阅读更多 →
3步搞定材料力学课后习题答案,面试必问避坑指南

3步搞定材料力学课后习题答案,面试必问避坑指南

3步搞定材料力学课后习题答案,面试必问避坑指南 配置环境就卡半天?别慌,这不仅是你的错觉,更是无数培训班学员和应届生的共同噩梦。刚打开IDE,依赖装不上,版本冲突报错,半小时过去了,代码一行没写,心态先崩了。这种痛苦,在准备 面试必问…

2026/9/22 2:57:45 阅读更多 →
3个案例揭秘上海积分落户系统性能优化

3个案例揭秘上海积分落户系统性能优化

3个案例揭秘上海积分落户系统性能优化 面试被问原理答不上来?别慌,这不仅是编程题,更是上海积分落户数据处理的实战考题。很多人卡在“积分怎么算”的逻辑上,其实核心是性能优化。 证书有效期校验是最大瓶颈…

2026/9/22 2:57:45 阅读更多 →
小度app新手避坑:5个性能优化技巧让你项目起飞

小度app新手避坑:5个性能优化技巧让你项目起飞

小度app新手避坑:5个性能优化技巧让你项目起飞 学会语法却不知怎么搭项目,是绝大多数初学者卡在入门阶段的真实写照。很多新手盯着《小度app》教程里的代码抄了一遍,跑通了Hello…

2026/9/22 2:57:45 阅读更多 →
荣耀路由pro 2源码拆解:3个关键坑与最佳实践

荣耀路由pro 2源码拆解:3个关键坑与最佳实践

荣耀路由pro 2源码拆解:3个关键坑与最佳实践 版本升级后 API 全变了,代码跑不起来?这大概是很多开发者在折腾 荣耀路由pro 2 时的噩梦。别慌,今天不聊虚的,直接扒开它的底层逻辑。我们结合 最佳实践…

2026/9/22 2:56:44 阅读更多 →

日新闻

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天 配置环境就卡半天?别怪机器慢,多半是你没选对工具链。在Java、Go或Python的项目现场, 手写实现…

2026/9/22 0:00:41 阅读更多 →
剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑 面试被问原理答不上来,是不是常态?别慌。很多开发者对着 GitHub 开源仓库里的代码发呆,看似简单实则暗藏玄机。今天这份【剑帝加点】速查手册,直接带你拆解核心实现,把面试必考的原理讲透。…

2026/9/22 0:00:41 阅读更多 →
手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优 复制来的代码跑不通不知道怎么调?别慌,这种“复制粘贴地狱”在开发圈太常见了。尤其是做 图片压缩网站…

2026/9/22 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/21 3:13:20 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/21 2:19:36 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/21 4:51:05 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/22 2:43:42 阅读更多 →