From 456b1bbf6666ab2424a644b383ffb1f4cbf1fdf4 Mon Sep 17 00:00:00 2001 From: Zhicheng Wu <150873423+newclearclear@users.noreply.github.com> Date: Thu, 19 Mar 2026 21:08:09 +0800 Subject: [PATCH] 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 | --- .../hooks/logic-hooks/use-row-selection.ts | 19 +++++++++++++++---- .../pages/dataset/dataset/dataset-table.tsx | 1 + web/src/pages/files/files-table.tsx | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/web/src/hooks/logic-hooks/use-row-selection.ts b/web/src/hooks/logic-hooks/use-row-selection.ts index e86fc9bb0c..af6cf37b5f 100644 --- a/web/src/hooks/logic-hooks/use-row-selection.ts +++ b/web/src/hooks/logic-hooks/use-row-selection.ts @@ -29,10 +29,21 @@ export function useSelectedIds>( 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 }; diff --git a/web/src/pages/dataset/dataset/dataset-table.tsx b/web/src/pages/dataset/dataset/dataset-table.tsx index 1cf29aadb8..950aa76727 100644 --- a/web/src/pages/dataset/dataset/dataset-table.tsx +++ b/web/src/pages/dataset/dataset/dataset-table.tsx @@ -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, diff --git a/web/src/pages/files/files-table.tsx b/web/src/pages/files/files-table.tsx index abc072185e..4c6f9d3a3b 100644 --- a/web/src/pages/files/files-table.tsx +++ b/web/src/pages/files/files-table.tsx @@ -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);