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
相关产品推荐
相关产品推荐

