diff --git a/apps/dashboard/shared/data-view/table-view/data-table.tsx b/apps/dashboard/shared/data-view/table-view/data-table.tsx index 12b03ef..e847784 100644 --- a/apps/dashboard/shared/data-view/table-view/data-table.tsx +++ b/apps/dashboard/shared/data-view/table-view/data-table.tsx @@ -39,8 +39,13 @@ export function DataTable({ // Persisted map of id → original row data across all pages const persistedMap = useRef>(new Map()) - // Current-page selection state that TanStack Table controls + // Current-page selection state that TanStack Table controls. + // A ref mirrors the latest value so onRowSelectionChange never derives the + // next selection from a stale closure (which would drop previously-checked + // rows and make multi-select behave like single-select). const [rowSelection, setRowSelection] = useState({}) + const rowSelectionRef = useRef({}) + rowSelectionRef.current = rowSelection // When the page/data changes, restore selection state for the new page from the map useEffect(() => { @@ -50,6 +55,7 @@ export function DataTable({ const id = String((row as Record)[rowKeyStr]) if (persistedMap.current.has(id)) restored[id] = true }) + rowSelectionRef.current = restored setRowSelection(restored) }, [data]) // eslint-disable-line react-hooks/exhaustive-deps @@ -128,7 +134,10 @@ export function DataTable({ }) }, onRowSelectionChange: (updater) => { - const next = typeof updater === "function" ? updater(rowSelection) : updater + // Derive from the ref (always current), not the render-time closure, + // so toggling a second row preserves earlier selections. + const next = typeof updater === "function" ? updater(rowSelectionRef.current) : updater + rowSelectionRef.current = next setRowSelection(next) if (selection) { // Sync current page into the persisted map