I am trying to make a nice Result View for looking at results from an SQL query. These results might contain thousands of results so React Virtualized was chosen to avoid rendering all the rows. The MultiGrid component was chosen so that the column names from the query are stickied to the top of the view. The component looks like this:
The MultiGrid has Dynamic Width, using Cell Measurer. The code is heavily inspired from the example here. My problem is that the component renders really slowly, and in the cases where the component has been rendered, then hidden, and then rendered again, the entire app freezes for a minute or more. There is obviously something that is rendered that should not be, but I am unsure where the problem lies. I tried clearing the CellMeasurerCache but that did not seem to work. The code for the MultiGrid is as follows:
const useStyles = makeStyles((theme) => ({
tableWrapper: {
minHeight: 200,
height: '100%',
width: '100%',
resize: 'vertical',
overflowY: 'hidden',
scrollbarWidth: 'none',
paddingBottom: theme.spacing(2),
},
gridCell: {
whiteSpace: 'nowrap',
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
paddingLeft: '1em',
paddingRight: '1em',
justifyContent: 'center',
},
}));
export default function ResultTable() {
const recordingContext = useContext(RecordingContext);
const classes = useStyles();
const result = recordingContext?.activeRecording?.queryOutput;
if (!result || !result.results) {
return (
<Typography style={{ padding: '8px 16px' }} variant="subtitle1">
No data to show
</Typography>
);
}
const { labels, types, values } = result.results;
function formatDate(d: Date) {
let month = `${d.getMonth() + 1}`;
let day = `${d.getDate()}`;
const year = d.getFullYear();
if (month.length < 2) month = `0${month}`;
if (day.length < 2) day = `0${day}`;
return [year, month, day].join('-');
}
function format(index: number, entry: unknown): string {
let datatype = 0;
if (types) {
datatype = types[index];
}
if (datatype === 12) {
if (typeof entry === 'number') {
const date = new Date(Number(entry));
return formatDate(date);
}
const date = new Date(String(entry));
return formatDate(date);
}
return String(entry);
}
const STYLE: CSSProperties = {
width: '100%',
fontFamily: 'raleway',
resize: 'none',
};
const cache = new CellMeasurerCache({
defaultWidth: 100,
fixedHeight: true,
});
type CellRendererType = {
rowIndex: number;
columnIndex: number;
style: React.CSSProperties;
parent: MeasuredCellParent;
};
function cellRenderer({
rowIndex,
parent,
columnIndex,
style,
}: CellRendererType) {
const content =
rowIndex === 0
? labels[columnIndex]
: format(columnIndex, values[rowIndex - 1][columnIndex]);
return (
<CellMeasurer
cache={cache}
parent={parent}
key={`${columnIndex}-${rowIndex}`}
columnIndex={columnIndex}
rowIndex={rowIndex}
>
<div
style={style}
key={`${columnIndex}-${rowIndex}`}
className={classes.gridCell}
>
{content}
</div>
</CellMeasurer>
);
}
return (
<div className={classes.tableWrapper}>
<div
style={{
minHeight: 201,
height: '100%',
width: '100%',
paddingBottom: 1,
}}
>
<AutoSizer>
{({ height, width }) => (
<MultiGrid
cellRenderer={cellRenderer}
columnWidth={cache.columnWidth}
columnCount={labels.length}
height={height}
deferredMeasurementCache={cache}
rowHeight={48}
rowCount={values.length + 1}
scrollToColumn={0}
scrollToRow={0}
fixedRowCount={1}
width={width}
style={STYLE}
styleTopRightGrid={{
fontWeight: 'bold',
width: '100%',
backgroundColor: '#404040',
fontSize: 14,
}}
styleBottomRightGrid={{
outline: 'none',
}}
overscanColumnCount={0}
overscanRowCount={20}
/>
)}
</AutoSizer>
</div>
</div>
);
}