AntD Table中rowClassName执行多次导致性能问题,如何解决?

AntD,Table用rowClassName控制行的背景色,包括选中行的背景色,选中行的index在onClick方法设置到useState。数据很多的情况下,tableRowClassName方法会执行多次,导致选择行时很慢

<Table dataSource={dataSource} columns={columns} size="small" bordered 
        loading={viewForm.loading} pagination={false} rowClassName={tableRowClassName} scroll={{ x: 1300, y: maxHeight}}
        onRow={(record, rowIndex) => ({
          onClick: () => onRowClick(record, rowIndex),
          onDoubleClick: () => getoPage(2, rowData),
        })}/>

const tableRowClassName = (record: any, index: number) => {
    console.info("tableRowClassName");
    if (viewForm.rowIndex === index) {
      return 'selected-row';
    }
    if (index % 2 === 1) {
      return 'light-blue-background';
    }
    
    if (record.StpKbn == 1) {
      return ' stop-distinguish-row';
    }
    if (record.SaihenCoNamUpdKbn == 1) {
      return ' disabled-distinguish-row';
    }
    return "";
  }

有什么方法在选择行时,变快一点吗?

阅读 1.3k
avatarAI BotBETA

在Ant Design的Table组件中,rowClassName函数会在每次渲染时针对每一行被调用,这确实可能导致性能问题,尤其是在处理大量数据时。为了优化性能,你可以考虑以下几种方法:

  1. 使用Memoization(记忆化):
    你可以使用React的useMemo或者一个第三方库(如reselect)来缓存rowClassName的结果。但是,由于rowClassName是一个基于props和当前行的函数,因此它的参数(如indexrecord)可能会在每次渲染时变化,这限制了memoization的有效性。尽管如此,如果viewForm.rowIndexdataSource没有频繁变化,记忆化仍然可能有所帮助。
  2. 减少不必要的渲染:
    确保你的组件树在不需要时不会重新渲染。使用React.memouseCallbackuseMemoPureComponent(如果你使用的是类组件)可以帮助减少不必要的渲染。
  3. 优化rowClassName函数:
    你的rowClassName函数目前检查了多个条件。如果可能的话,考虑简化逻辑或使用更快的替代方法。例如,如果你可以预计算每行的类名,并将其作为数据的一部分传递给Table,那么这将避免在每次渲染时重新计算。
  4. 使用CSS代替JS逻辑:
    如果可能的话,考虑使用CSS而不是JavaScript来处理行的样式。例如,你可以使用CSS的:nth-child选择器来处理奇偶行的背景色,而不需要在JavaScript中检查index
  5. 分页:
    如果你的表有很多行,考虑使用分页来减少一次渲染中显示的行数。这将直接减少rowClassName函数被调用的次数。
  6. 使用虚拟滚动:
    对于非常大的数据集,虚拟滚动(也称为窗口化)是一个有效的性能优化技术。它只渲染视口中可见的行,而不是整个数据集。这可以显著减少rowClassName的调用次数和DOM节点的数量。
  7. 检查其他性能瓶颈:
    确保性能问题不是由其他因素引起的,如复杂的列渲染器、大型单元格内容或昂贵的onRow事件处理程序。

在你的具体情况下,由于你需要在onClick事件中更新viewForm.rowIndex,这可能会触发整个表的重新渲染。为了优化这一点,你可以考虑使用React的useStateuseCallback来确保onRowClicktableRowClassName只在依赖项变化时更新。但是,请注意,由于indexrecord对于每一行都是唯一的,所以完全避免重新渲染可能是不现实的。

以下是一个使用useCallback的示例,但请注意,它可能不会完全解决性能问题,因为它仍然需要在每次渲染时调用tableRowClassName

const [viewForm, setViewForm] = useState({ rowIndex: null, /* ...其他状态... */ });

const onRowClick = useCallback((record, rowIndex) => {
  setViewForm(prevState => ({ ...prevState, rowIndex }));
}, []);

const tableRowClassName = useCallback((record: any, index: number) => {
  // ... 你的逻辑 ...
}, [viewForm.rowIndex]); // 注意这里只依赖于viewForm.rowIndex,但可能不足以避免所有重新渲染

// ... 你的Table组件 ...
1 个回答

1.可以采用操作dom手段来处理

          onRow={(record, i) => ({
            onClick: (event) => {
              //  先清除已选高亮行
              const HighlightRow =document.getElementsByClassName("HighlightRow")![0];
              if (HighlightRow) {
                HighlightRow.classList.remove("HighlightRow");
              }
              //  增加高亮行 className
              let target = event.currentTarget as HTMLElement;
              while (target) {
                const classList = target!.classList;
                if (classList.contains("ant-table-row")) {
                  classList.add("HighlightRow");
                  break;
                }
                target = target.parentElement!;
              }
            },

2.通过 virtual 开启虚拟滚动 只会重新渲染可视区域内的行
3.分页也是一种选择

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏