You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 03:35:36