Skip to content

fix(Table): keep select-all within the current page when reserveSelectedRowOnPaginate is false - #4472

Open
lnuxe wants to merge 1 commit into
Tencent:developfrom
lnuxe:fix/table-select-all-current-page
Open

lnuxe wants to merge 1 commit into
Tencent:developfrom
lnuxe:fix/table-select-all-current-page

Conversation

@lnuxe

@lnuxe lnuxe commented Nov 10, 2026 •

Copy link
Copy Markdown

🤔 这个 PR 的性质是?

  • 日常 bug 修复
  • 新特性提交
  • 文档改进
  • 演示代码改进
  • 组件样式/交互改进
  • CI/CD 改进
  • 重构
  • 代码风格优化
  • 测试用例
  • 分支合并
  • 其他

🔗 相关 Issue

💡 需求背景和解决方案

reserveSelectedRowOnPaginate={false} 的语义是「全部选中操作停留在当前页,不跨分页」(见 table.md 第 126 行的 API 说明),但表头全选实际选中了全部数据,表头全选框的 checked/indeterminate 也是按全部数据计算的。

Root cause | 根因

packages/components/table/hooks/useRowSelect.tsx 中:

// 树形数据时,展开所有树节点行;否则使用原始 data
const allTreeRows = useMemo(() => {
  if (!treeDataMap || treeDataMap.size === 0) return data;   // 普通表格 = 完整 data
  ...
}, [data, treeDataMap]);

// 当前可见行:树形数据优先,否则使用分页后的数据
const currentRows = useMemo(() => allTreeRows || currentPaginateData, [allTreeRows, currentPaginateData]);

普通(非树形)表格下 allTreeRows 恒等于完整 data,而数组在 || 中恒为真值,导致后面的 currentPaginateData 分支是死代码:
currentRows = 全部 6 条 / 10 条数据,而不是当前页的 2 条 / 5 条。于是:

  • handleSelectAll() 用 enabledRows(= currentRows 过滤 disabled)取 key → 全选写入全部数据;
  • renderCheckAll() 的 allEnabledSelected / isChecked / isIndeterminate 也都基于全部数据 → 表头状态与当前页不符。

另外两处会放大问题:

  1. 同步 currentPaginateData 的 effect 只按 [data, reserveSelectedRowOnPaginate] 触发,且无条件 data.slice(...);远程分页(data 已是当前页数据)会再切一次导致当前页变成空数组,因此不能在修 currentRows 时直接启用它。
  2. handleSelectAll() 里「保留禁用行 key」的过滤条件是 !enabledRowsSet.has(id),它同时把其他分页的 key 当作禁用行保留了下来,导致 false 模式下全选仍然带着其它页的 key。

Fix | 修复

只改 useRowSelect.tsx,保持 API、默认值(true)与树形表格语义不变:

  1. 按组件现有约定判断「是否由表格自身做本地分页」,与 usePagination 的 shouldPaginate 保持一致(pagination 存在、未设置 disableDataPage、且 data.length > pageSize);
  2. currentRows 改为:树形数据 / 跨页保留选中 / 未做本地分页时用全部数据,否则用当前页数据;
  3. currentPaginateData 的同步 effect 改用同一个判断,受控分页取 pagination.current,未做本地分页时直接使用 data(不再二次切片,避免破坏远程分页);
  4. handleSelectAll() 中,reserveSelectedRowOnPaginate=false 时只保留当前页的禁用行 key,其他分页的 key 不再参与全选。

🧪 Reproducible case | 复现用例

新增 packages/components/table/__tests__/row-select-paginate.test.tsx(6 行数据、pageSize: 2、reserveSelectedRowOnPaginate={false}),覆盖:

  • 第 1 页 / 第 2 页全选只选中当前页,且表头变 checked;
  • 取消全选清空当前页;
  • 传入其他页的 key 时,全选会丢弃这些 key;
  • 切页后全选跟随新一页;
  • reserveSelectedRowOnPaginate={true}、无分页、远程分页(data 已是当前页)三种对照不回归。

☑️ Verification | 验证

真实浏览器(Chrome,1000×780@2x):10 条数据、pageSize: 5、reserveSelectedRowOnPaginate={false},第 1 页只有 5 行,点击表头全选框,读取 onSelectChange 写入 DOM 的 selectedRowKeys。

Before(develop)
selectedRowKeys (10): 1 … 10
当前页只有 5 行,却选中了 10 条
After(本 PR)
selectedRowKeys (5): 1, 2, 3, 4, 5
只选中当前页的 5 行
before: select-all on page 1 selects all 10 rows after: select-all on page 1 selects only the 5 rows of the page

浏览器侧原始输出:

// before(develop,点击表头全选后)
{"visibleRows":5,"selectedText":"selectedRowKeys (10): 1, 2, 3, 4, 5, 6, 7, 8, 9, 10","count":"10","keys":"1,2,3,4,5,6,7,8,9,10","headerCheckboxClass":"t-checkbox t-is-checked","visibleRowIds":["1","2","3","4","5"],"pageErrors":[]}

// after(本 PR,同一页面同一操作)
{"visibleRows":5,"selectedText":"selectedRowKeys (5): 1, 2, 3, 4, 5","count":"5","keys":"1,2,3,4,5","headerCheckboxClass":"t-checkbox t-is-checked","visibleRowIds":["1","2","3","4","5"],"pageErrors":[]}

单测(row-select-paginate.test.tsx,同一份用例在修复前后各跑一次):

# develop(未修复)
× select-all only selects the rows of the current page      → expected [ 1, 2, 3, 4, 5, 6 ] to deeply equal [ 1, 2 ]
× select-all on a non-first page only selects that page     → expected [ 1, 2, 3, 4, 5, 6 ] to deeply equal [ 3, 4 ]
× drops selected keys that are outside the current page     → expected [ 1, 2, 3, 4, 5, 6 ] to deeply equal [ 1, 2 ]
× select-all follows the page after pagination changes      → expected [ 1, 2, 3, 4, 5, 6 ] to deeply equal [ 1, 2 ]
Test Files  1 failed (1)   Tests  4 failed | 4 passed (8)

# 本 PR
✓ packages/components/table/__tests__/row-select-paginate.test.tsx (8 tests)
✓ packages/components/table/__tests__/enhanced-table.test.tsx (4 tests)
✓ packages/components/table/__tests__/table.test.tsx (43 tests)
Test Files  3 passed (3)   Tests  55 passed (55)

执行的命令:

npx vitest run packages/components/table/                              # 55 passed
npx vitest run packages/components/table/__tests__/row-select-paginate.test.tsx
npx eslint packages/components/table/hooks/useRowSelect.tsx packages/components/table/__tests__/row-select-paginate.test.tsx --max-warnings 0

📝 更新日志

  • 本条 PR 不需要纳入 Changelog

tdesign-react

  • fix(Table): 修复 reserveSelectedRowOnPaginate={false} 时表头全选包含其他分页数据、全选框状态按全部数据计算的问题 @lnuxe

☑️ 请求合并前的自查清单

  • 文档已补充或无须补充
  • 代码演示已提供或无须提供
  • TypeScript 定义已补充或无须补充
  • Changelog 已提供或无须提供

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Table] reserveSelectedRowOnPaginate=false 时表头全选包含其他页数据

1 participant