使用MUI Table结合TableVirtuoso实现排序时需手动滚动才生效的问题求助
MUI Table结合TableVirtuoso实现排序时需手动滚动才生效的问题求助
最近我在尝试用MUI Table组件搭配TableVirtuoso实现一个虚拟化表格,同时想给它加上排序功能,参考了官方文档里的排序示例。不过那个示例里的排序逻辑是在渲染行的时候处理的,但TableVirtuoso的用法是直接传入完整的数据源数组,然后通过itemContent参数指定每一行的渲染函数。
我的思路是:当数据源、排序方向或者排序列发生变化时,用useState和useEffect钩子先把数据源数组排好序,再传给TableVirtuoso组件。现在的问题是,虽然数组确实已经被正确排序了,但UI上完全看不到变化,必须手动滚动表格才会更新;就算排序前表格已经滚动到下方,我调用了scrollToIndex(0)也没法让UI同步更新。
我感觉修改数据源数组之后,并没有触发TableVirtuoso内部的重渲染,和预期的不一样。想请教一下各位,有没有办法在排序操作时强制触发表格的重渲染?或者有没有其他更合适的方案来实现这个虚拟化表格的排序功能?
组件代码
'use strict'; import React, { forwardRef, useState, useEffect, useRef, } from 'react'; import Table from '@mui/material/Table'; import TableBody from '@mui/material/TableBody'; import TableCell from '@mui/material/TableCell'; import TableContainer from '@mui/material/TableContainer'; import TableHead from '@mui/material/TableHead'; import TableRow from '@mui/material/TableRow'; import TableSortLabel from '@mui/material/TableSortLabel'; import Paper from '@mui/material/Paper'; import { TableVirtuoso } from 'react-virtuoso'; import { useTranslation } from './contexts/TranslationContext'; export const VirtualTable = (props) => { // get props const { columnConfig, data: propData } = props; // get translation hook const { t, locale } = useTranslation(); // get virtuoso ref const virtuoso = useRef(null); // get data state const [data, setData] = useState(propData); // get order state const [order, setOrder] = useState('asc'); // get order by state const [orderBy, setOrderBy] = useState(columnConfig[0].dataKey); // handle change of propData, order and orderBy and sort data useEffect(() => { setData((oldData) => oldData.sort(getComparator(order, orderBy))); }, [propData, order, orderBy]); // prepare sort comparators const descendingComparator = (a, b, orderBy) => { if (b[orderBy] < a[orderBy]) { return -1; } if (b[orderBy] > a[orderBy]) { return 1; } return 0; }; const getComparator = (order, orderBy) => { return order === 'desc' ? (a, b) => descendingComparator(a, b, orderBy) : (a, b) => -descendingComparator(a, b, orderBy); }; // prepare Virtuoso components const virtuosoComponents = { Scroller: forwardRef((props, ref) => ( <TableContainer component={Paper} {...props} ref={ref} /> )), Table: (props) => <Table {...props} sx={{ borderCollapse: 'separate' }} />, TableHead: forwardRef((props, ref) => <TableHead {...props} sx={{ boxShadow: 3 }} ref={ref} />), TableRow: ({ item: _, ...props }) => <TableRow {...props} hover={true} />, TableBody: forwardRef((props, ref) => <TableBody {...props} ref={ref} />), }; // event handler to handle onClick for sorting table const handleSort = (columnDataKey) => { const isAsc = orderBy === columnDataKey && order === 'asc'; setOrder(isAsc ? 'desc' : 'asc'); setOrderBy(columnDataKey); virtuoso.current.scrollToIndex(0); }; // format cell data according to type const getCellData = (row, column) => { // switch type switch(column.type) { case 'string': return row[column.dataKey]; case 'date': if(row[column.dataKey] === '') { return ''; } return (new Date(row[column.dataKey])).toLocaleDateString(locale, {day: '2-digit', month: '2-digit', year: 'numeric'}); case 'actions': return ''; default: return t('Column type "{{type}}" is not valid', {type: column.type}); } }; // prepare fixed header const getFixedHeader = () => { return ( <TableRow> { columnConfig.map((column) => ( <TableCell key={column.dataKey} align={column.align} sx={{ width: typeof column.width !== 'undefined' ? column.width : undefined, backgroundColor: 'background.paper', }} > {typeof column.sortable !== 'undefined' && column.sortable === false ? column.label : <TableSortLabel active={orderBy === column.dataKey} direction={orderBy === column.dataKey ? order : 'asc'} onClick={() => handleSort(column.dataKey)} > {column.label} </TableSortLabel> } </TableCell> )) } </TableRow> ); }; // prepare row content const getRowContent = (_index, row) => { return ( <> {columnConfig.map((column) => ( <TableCell key={column.dataKey} align={column.align} > {getCellData(row, column)} </TableCell> ))} </> ); }; // render jsx return ( <Paper style={{ width: '100%', height: 'calc(100vh - 160px' }} > <TableVirtuoso ref={virtuoso} data={data} fixedHeaderContent={getFixedHeader} itemContent={getRowContent} components={virtuosoComponents} overscan={{ main: 5, reverse: 5 }} /> </Paper> ); };
传入的columnConfig属性
const columnConfig = [ { label: t('Begin'), dataKey: 'begin', align: 'left', width: '10%', type: 'date', }, { label: t('End'), dataKey: 'end', align: 'left', width: '10%', type: 'date', sortable: false, }, { label: t('Event'), dataKey: 'name', align: 'left', type: 'string', }, { label: t('Location'), dataKey: 'location', align: 'left', width: '15%', type: 'string', }, { label: t('Actions'), dataKey: 'actions', align: 'right', width: '15%', type: 'actions', sortable: false, actions: [ ], }, ];
传入的data属性
const data = Array.from({ length: 100 }, (_, index) => { const now = new Date(); const begin = (new Date()).setDate(now.getDate() + index); return { id: index, begin: begin, end: '', name: `Event ${index}`, location: `Location ${index}`, actions: '', }; });
备注:内容来源于stack exchange,提问作者bohrsty
相关产品推荐
相关产品推荐

