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

React TanStack Virtualizer getVirtualItems在RTA+Jest测试中返回空数组

React Testing Library + Jest测试TanStack Virtualizer表格时getVirtualItems()返回空数组的解决方案

问题背景

使用React Testing Library和Jest测试基于React TanStack Virtualizer开发的表格时,浏览器运行正常,但测试中rowVirtualizer.getVirtualItems()返回空数组——尽管已成功模拟数据,且table.getRowModel()生成的rows数组正常。

核心原因

TanStack Virtualizer依赖滚动容器的可视尺寸计算需要渲染的虚拟项,而Jest/jsdom测试环境默认不会为元素设置真实宽高,导致Virtualizer无法识别可视区域范围,因此返回空数组。此外,测试环境中滚动容器的ref可能未正确挂载,或元素尺寸为0,也会触发该问题。

解决方案

1. 手动设置滚动容器尺寸

在测试中给表格容器设置明确高度,让Virtualizer能计算可视区域:

// 假设表格容器添加了data-testid="table-container"
const tableContainer = screen.getByTestId('table-container');
// 方式一:通过style设置高度
tableContainer.style.height = '500px';
// 方式二:直接定义clientHeight属性
Object.defineProperty(tableContainer, 'clientHeight', { value: 500 });

2. 触发尺寸变化事件,强制重新计算

设置容器尺寸后,触发window的resize事件,让Virtualizer重新计算虚拟项:

import { fireEvent, waitFor } from '@testing-library/react';

// 触发resize事件
fireEvent(window, new Event('resize'));

// 等待虚拟项渲染完成后断言
await waitFor(() => {
  expect(screen.getByText('第一行模拟数据文本')).toBeInTheDocument();
});

3. 调整测试环境下的行高测量逻辑

原代码中measureElement依赖浏览器环境的getBoundingClientRect(),测试环境中该方法返回高度可能为0。可通过mocknavigator.userAgent,让测试环境使用固定的estimateSize而非动态测量:

beforeAll(() => {
  // 模拟Firefox UA,让measureElement变为undefined,使用固定行高
  Object.defineProperty(navigator, 'userAgent', {
    value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
    writable: true,
  });
});

4. 确认滚动容器Ref已正确挂载

确保测试中表格容器元素已渲染,Ref已赋值:

// 组件中给容器添加data-testid
<div ref={tableContainerRef} data-testid="table-container">
  {/* 虚拟行渲染内容 */}
</div>

// 测试中验证容器存在
expect(screen.getByTestId('table-container')).toBeInTheDocument();

待测试代码参考

const table = useReactTable<InvoiceRead>({
  data: flatData,
  columns,
  state: {
    rowSelection,
  },
  getCoreRowModel: getCoreRowModel(),
  onRowSelectionChange: setRowSelection,
  debugTable: true,
  enableRowSelection: true,
});

const { rows } = table.getRowModel();

const rowVirtualizer = useVirtualizer({
  count: rows.length,
  estimateSize: () => 33,
  getScrollElement: () => tableContainerRef.current,
  measureElement:
    typeof window !== "undefined" &&
    navigator.userAgent.indexOf("Firefox") === -1
      ? (element) => element?.getBoundingClientRect().height
      : undefined,
  overscan: 5,
});

虚拟行渲染代码参考

{rowVirtualizer.getVirtualItems().map((virtualRow) => {
  const row = rows[virtualRow.index] as Row<InvoiceRead>;
  return (
    <InvoiceTableRow
      row={row}
      isNewRow={newInvoiceIds.includes(row.original.invoiceId)}
      isSelectedRow={row.getIsSelected()}
      virtualRow={virtualRow}
      ref={(node) => rowVirtualizer.measureElement(node)}
      onCellClick={onCellClick}
      style={{
        transform: `translateY(${virtualRow.start}px)`,
        backgroundColor:
          row.index % 2 === 0 ? "white" : "rgb(236, 237, 238, .5)",
      }}
    />
  );
})}

内容的提问来源于stack exchange,提问作者sam-garretson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 01:22:42