react通过react-window实现虚拟列表
VirtualTable.tsx
import { TableProps } from "antd";
import { Table } from "antd";
import classNames from "classnames";
import _ from "lodash";
import ResizeObserver from "rc-resize-observer";
import React, { ReactNode, useEffect, useRef, useState } from "react";
import { VariableSizeGrid as Grid } from "react-window";
type Props = {
columns: any;
dataSource: any[];
scroll: Record<string, any>;
loading?: boolean;
tableTitle: () => ReactNode;
};
export const VirtualTable = (props: Props) => {
const { columns, scroll, tableTitle, loading } = props;
const [tableWidth, setTableWidth] = useState(0);
const mergedColumns: any = columns!.map((column: any) => {
if (column.width) {
return column;
}
return {
...column,
width: 200,
};
});
const gridRef = useRef<any>();
const [connectObject] = useState<any>(() => {
const obj = {};
Object.defineProperty(obj, "scrollLeft", {
get: () => {
if (gridRef.current) {
return gridRef.current?.state?.scrollLeft;
}
return null;
},
set: (scrollLeft: number) => {
if (gridRef.current) {
gridRef.current.scrollTo({ scrollLeft });
}
},
});
return obj;
});
const resetVirtualGrid = () => {
gridRef.current?.resetAfterIndices({
columnIndex: 0,
shouldForceUpdate: true,
});
};
useEffect(() => resetVirtualGrid, [tableWidth]);
const renderVirtualList: any = (rawData: object[], { scrollbarSize, ref, onScroll }: any) => {
ref.current = connectObject;
const totalHeight = rawData.length * 54;
return (
<Grid
ref={gridRef}
className="virtual-grid"
columnCount={mergedColumns.length}
columnWidth={(index: number) => {
const { width } = mergedColumns[index];
return totalHeight > (scroll?.y as number) && index === mergedColumns.length - 1
? (width as number) - scrollbarSize - 1
: (width as number);
}}
height={scroll!.y as number}
rowCount={rawData.length}
rowHeight={() => 54}
width={tableWidth}
onScroll={({ scrollLeft }: { scrollLeft: number }) => {
onScroll({ scrollLeft });
}}
>
{({ columnIndex, rowIndex, style }: { columnIndex: number; rowIndex: number; style: React.CSSProperties }) => (
<div
className={classNames("virtual-table-cell p-2 box-border", {
"virtual-table-cell-last": columnIndex === mergedColumns.length - 1,
})}
style={{
...style,
}}
>
{mergedColumns[columnIndex].render
? mergedColumns[columnIndex].render(
(rawData[rowIndex] as any)[(mergedColumns as any)[columnIndex].dataIndex],
rawData[rowIndex]
)
: (rawData[rowIndex] as any)[(mergedColumns as any)[columnIndex].dataIndex]}
</div>
)}
</Grid>
);
};
return (
<ResizeObserver
onResize={({ width }) => {
setTableWidth(width);
}}
>
<Table
{...props}
className="virtual-table"
columns={mergedColumns}
pagination={false}
loading={loading}
components={{
body: renderVirtualList,
}}
title={tableTitle}
/>
</ResizeObserver>
);
};
使用
<VirtualTable
columns={columns}
dataSource={list}
scroll={{ y: document.body.clientHeight - 310, x: 2000 }}
loading={isLoading}
tableTitle={() => (
<div className="flex justify-between w-full">
<Pagination
current={page}
size="small"
total={data?.total}
showTotal={(total) => `共 ${total} 条`}
pageSize={10000}
showQuickJumper
onChange={(page) => setPage(page)}
/>
<div className="flex-auto flex justify-end items-center">
<Button className="mr-2" type="primary" onClick={toggleCreateOpen}>
新增数据
</Button>
<Input.Search
placeholder="搜索当页数据"
className="mr-1 w-48"
allowClear
onChange={_.debounce((e) => setKeywords(e.target.value), 300)}
/>
</div>
</div>
)}
/>
上一篇:js简单使用定时任务