Reputation: 103
Using @nextui-org/react
I want to display records in a Table with 300+ columns.
The server response time of the API to fetch the data is around 200 ms.
It takes around 10 seconds to display the records having a page size of 25 (I've used serverside pagination).
When I reduce the column size to 15 or so, it loads the data within a second.
I tried the react-virtualized
package to virtualize the columns but, I think it doesn't suppport @nextui-org/react Table
.
I am getting an error...
TypeError: rowGetter is not a function
Here is my full code.
import React, { useCallback, useEffect, useMemo, useState } from "react";
import {
TableHeader,
TableColumn,
TableRow,
TableCell,
Pagination,
Spinner,
} from "@nextui-org/react";
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/outline";
import { AutoSizer, Column, Table as VirtualizedTable } from 'react-virtualized';
const ROWS_PER_PAGE = 25;
const DataTable = ({
columns,
items,
isLoading,
error,
refetch,
params,
setParams,
totalRows,
}) => {
const [sortDescriptors, setSortDescriptors] = useState(() =>
params.sortColumns.map((item, i) => ({
column: item,
direction: params.sortDirections[i] ?? "ASC",
}))
);
const [page, setPage] = useState(1);
const pages = totalRows ? Math.ceil(totalRows / ROWS_PER_PAGE) : 0;
useEffect(() => {
refetch(params);
}, [params]);
useEffect(() => {
const offset = (page - 1) * ROWS_PER_PAGE;
setParams((prev) => ({ ...prev, offset }));
}, [page]);
useEffect(() => {
setParams((prev) => ({
...prev,
sortColumns: sortDescriptors.map((i) => i.column),
sortDirections: sortDescriptors.map((i) => i.direction),
}));
}, [sortDescriptors]);
const handleSorting = useCallback(
(e, column) => {
if (column?.sortable) {
setSortDescriptors((prev) => {
const existingDescriptor = prev.find(
(desc) => desc.column === column.uid
);
if (existingDescriptor) {
existingDescriptor.direction =
existingDescriptor.direction === "ASC" ? "DESC" : "ASC";
return [...prev];
} else {
return [...prev, { column: column.uid, direction: "ASC" }];
}
});
}
},
[sortDescriptors]
);
const rowGetter = ({ index }) => items[index];
const renderHeader = ({ columnData }) => {
const { column } = columnData;
return (
<TableColumn
key={column.uid}
allowsSorting={false}
onClick={(e) => handleSorting(e, column)}
style={{
width: column.width ? column.width : "",
minWidth: column.minWidth ? column.minWidth : "",
maxWidth: column.maxWidth ? column.maxWidth : "",
backgroundColor: "#bfdbfe",
...column.style,
}}
className={`${
column?.sortable ? "cursor-pointer" : "cursor-default"
}`}
>
<div className={`inline-block w-full ${column.align ? column.align : ""}`}>
{column.name}
</div>
{column.sortable && (
<SortIcon
column={column}
direction={
sortDescriptors.find((desc) => desc.column === column.uid)
?.direction
}
/>
)}
</TableColumn>
);
};
const renderRow = ({ index, key, style }) => {
const item = rowGetter({ index });
return (
<TableRow key={key} style={style}>
{columns.map((column) => (
<TableCell
key={column.uid}
className={`${column.align ? column.align : ""}`}
style={{
width: column.width ? column.width : "",
minWidth: column.minWidth ? column.minWidth : "",
maxWidth: column.maxWidth ? column.maxWidth : "",
}}
>
{column.hasOwnProperty("renderCell")
? column.renderCell(item, column.uid)
: item[column.uid]}
</TableCell>
))}
</TableRow>
);
};
return (
<div style={{ width: "100%", height: "100%" }}>
<AutoSizer>
{({ height, width }) => (
<VirtualizedTable
width={width}
height={height}
headerHeight={50}
rowHeight={40}
rowCount={items.length}
rowGetter={rowGetter}
rowRenderer={renderRow}
overscanRowCount={5}
>
{columns.map((column) => (
<Column
key={column.uid}
label={column.name}
dataKey={column.uid}
width={column.width || 100}
minWidth={column.minWidth || 50}
maxWidth={column.maxWidth || 200}
headerRenderer={renderHeader}
columnData={{ column }}
/>
))}
</VirtualizedTable>
)}
</AutoSizer>
<Pagination
showControls
classNames={{ cursor: "bg-primary text-background" }}
color="default"
page={page}
total={pages}
variant="light"
onChange={setPage}
/>
{isLoading && <Spinner label="Loading..." />}
{error && <div>{error.message}</div>}
</div>
);
};
const SortIcon = ({ column, direction }) => {
const iconClassName = `w-3 mb-px text-inherit inline-block transition-transform-opacity data-[visible=true]:opacity-100 group-data-[hover=true]:opacity-100 data-[direction=ascending]:rotate-180 ml-0 absolute ${
!direction ? "opacity-0" : "opacity-100"
}`;
return direction === "DESC" ? (
<ChevronDownIcon className={iconClassName} />
) : (
<ChevronUpIcon className={iconClassName} />
);
};
export default DataTable;
Upvotes: 0
Views: 171