使用Shadcn+Tanstack Table在组件外获取选中行触发无限循环的解决方法
解决Shadcn + Tanstack Table外部访问选中行导致的无限循环问题
问题根源
你遇到的无限循环,核心原因是table.getSelectedRowModel().rows每次组件渲染都会返回新的数组引用——哪怕选中的行内容完全没变化。这会导致监听rows的useEffect反复触发,调用setSelectedRows更新父组件状态;父组件状态变化又会触发子组件重新渲染,最终形成循环。
替代方案(无需useRef/onRowSelectionChange)
方案1:用useMemo缓存选中行,基于行ID判断更新
通过useMemo根据选中行的ID集合缓存转换后的原始行数组,只有当ID集合变化时才重新生成数组,避免不必要的更新触发。
function TableComponent ({columns, data, paginationProps, setSelectedRows}){ const manualPagination = useMemo(() => !!paginationProps, [paginationProps]); const [rowSelection, setRowSelection] = useState({}); const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: manualPagination ? paginationProps?.rowsPerPage || 0 : data.length, }); const table = useReactTable({ data, columns: columns, pageCount: manualPagination ? paginationProps?.totalPages : undefined, manualPagination, state: { rowSelection, pagination, }, getRowId(originalRow: any) { return originalRow.id; }, getCoreRowModel: getCoreRowModel(), onPaginationChange: (updater) => { setPagination(updater); }, onRowSelectionChange: (updater) => { setRowSelection(updater); }, getPaginationRowModel: getPaginationRowModel(), }); // 获取选中行的ID集合,作为依赖判断更新 const selectedRowIds = table.getState().rowSelection; // 缓存转换后的原始行数组,仅当ID集合或数据源变化时更新 const selectedOriginalRows = useMemo(() => { return data.filter(row => selectedRowIds[row.id]); }, [data, selectedRowIds]); useEffect(() => { setSelectedRows?.(selectedOriginalRows); }, [selectedOriginalRows, setSelectedRows]); }
方案2:直接监听内部rowSelection状态
既然rowSelection是组件内部维护的useState状态,直接监听它的变化来更新父组件状态——它只有在选中操作触发setRowSelection时才会变化,不会因为组件重新渲染产生新引用,从根源上避免循环。
function TableComponent ({columns, data, paginationProps, setSelectedRows}){ const manualPagination = useMemo(() => !!paginationProps, [paginationProps]); const [rowSelection, setRowSelection] = useState({}); const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: manualPagination ? paginationProps?.rowsPerPage || 0 : data.length, }); const table = useReactTable({ data, columns: columns, pageCount: manualPagination ? paginationProps?.totalPages : undefined, manualPagination, state: { rowSelection, pagination, }, getRowId(originalRow: any) { return originalRow.id; }, getCoreRowModel: getCoreRowModel(), onPaginationChange: (updater) => { setPagination(updater); }, onRowSelectionChange: (updater) => { setRowSelection(updater); }, getPaginationRowModel: getPaginationRowModel(), }); useEffect(() => { // 根据rowSelection过滤出原始行 const selectedRows = data.filter(row => rowSelection[row.id]); setSelectedRows?.(selectedRows); }, [rowSelection, data, setSelectedRows]); }
方案说明
- 方案1通过
useMemo缓存结果,确保只有选中行ID或数据源变化时才更新父组件状态,避免无意义的渲染循环。 - 方案2直接依赖内部维护的
rowSelection状态,它的引用稳定,只有实际选中操作才会触发更新,彻底切断循环触发的条件。
内容的提问来源于stack exchange,提问作者Bouaziz Mohamed Cherif
相关产品推荐
相关产品推荐

