fix: row selection leaks across pages in dataset and file list tables (#13668)

### What problem does this PR solve?

When using pagination in the Dataset file list or File Manager,
selecting row N on page 1 would incorrectly cause row N on page 2 (and
subsequent pages) to also appear selected. This is a state pollution
bug.

### Root Cause

TanStack React Table defaults to using array indices (0, 1, 2...) as
`rowSelection` keys. With server-side (manual) pagination, each page's
rows start from index 0, so a selection like `{2: true}` on page 1 also
matches index 2 on every other page.

### Fix

- Added `getRowId: (row) => row.id` to `useReactTable` in both
`DatasetTable` and `FilesTable`, so selection state is keyed by unique
document/file IDs instead of positional indices.
- Updated the `useSelectedIds` helper to support ID-based selection keys
while maintaining backward compatibility with index-based keys.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)

### Files Changed

| File | Change |
|------|--------|
| `web/src/pages/dataset/dataset/dataset-table.tsx` | Added `getRowId`
to table config |
| `web/src/pages/files/files-table.tsx` | Added `getRowId` to table
config |
| `web/src/hooks/logic-hooks/use-row-selection.ts` | Updated
`useSelectedIds` to handle ID-based selection |
This commit is contained in:
Zhicheng Wu
2026-03-19 21:08:09 +08:00
committed by GitHub
parent e1dbfb8a9c
commit 456b1bbf66
3 changed files with 17 additions and 5 deletions

View File

@ -29,10 +29,21 @@ export function useSelectedIds<T extends Array<{ id: string }>>(
list: T,
) {
const selectedIds = useMemo(() => {
const indexes = Object.keys(rowSelection);
return list
.filter((x, idx) => indexes.some((y) => Number(y) === idx))
.map((x) => x.id);
// When using getRowId, rowSelection keys are IDs, not indices
const selectionKeys = Object.keys(rowSelection);
// Check if keys are numeric (index-based) or string IDs
const isIndexBased = selectionKeys.every((key) => !isNaN(Number(key)));
if (isIndexBased) {
// Legacy index-based selection
return list
.filter((x, idx) => selectionKeys.some((y) => Number(y) === idx))
.map((x) => x.id);
} else {
// ID-based selection (when getRowId is used)
return selectionKeys.filter((id) => list.some((item) => item.id === id));
}
}, [list, rowSelection]);
return { selectedIds };

View File

@ -114,6 +114,7 @@ export function DatasetTable({
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
getRowId: (row) => row.id, // Use document ID instead of row index
manualPagination: true, //we're doing manual "server-side" pagination
state: {
sorting,

View File

@ -260,7 +260,7 @@ export function FilesTable({
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
getRowId: (row) => row.id, // Use file ID instead of row index
manualPagination: true, //we're doing manual "server-side" pagination
enableRowSelection(row) {
return !isKnowledgeBaseType(row.original.source_type);