everyone! I use Material React table in my project and the problem is: if I scroll the table and after that I resize any column, table data automatically goes back to the top. So I need to scroll it down again. Like in this example: https://www.material-react-table.com/docs/examples/virtualized
I tried to find any solution in Column Resizing Guide, but still. Is there any way to prevent such a behavior of a table?
import { useEffect, useMemo, useRef, useState } from 'react';
import {
MaterialReactTable,
useMaterialReactTable,
type MRT_ColumnDef,
type MRT_SortingState,
type MRT_RowVirtualizer,
} from 'material-react-table';
import { makeData, type Person } from './makeData';
const Example = () => {
const columns = useMemo<MRT_ColumnDef<Person>[]>(
() => [
{
accessorKey: 'firstName',
header: 'First Name',
size: 150,
},
{
accessorKey: 'middleName',
header: 'Middle Name',
size: 170,
},
{
accessorKey: 'lastName',
header: 'Last Name',
size: 150,
},
{
accessorKey: 'email',
header: 'Email Address',
size: 300,
},
{
accessorKey: 'phoneNumber',
header: 'Phone Number',
size: 250,
},
{
accessorKey: 'address',
header: 'Address',
size: 300,
},
{
accessorKey: 'zipCode',
header: 'Zip Code',
},
{
accessorKey: 'city',
header: 'City',
size: 220,
},
{
accessorKey: 'state',
header: 'State',
},
{
accessorKey: 'country',
header: 'Country',
size: 350,
},
{
accessorKey: 'petName',
header: 'Pet Name',
},
{
accessorKey: 'age',
header: 'Age',
},
{
accessorKey: 'salary',
header: 'Salary',
},
{
accessorKey: 'dateOfBirth',
header: 'Date of Birth',
},
{
accessorKey: 'dateOfJoining',
header: 'Date of Joining',
},
{
accessorKey: 'isActive',
header: 'Is Active',
},
],
[],
);
//optionally access the underlying virtualizer instance
const rowVirtualizerInstanceRef = useRef<MRT_RowVirtualizer>(null);
const [data, setData] = useState<Person[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [sorting, setSorting] = useState<MRT_SortingState>([]);
useEffect(() => {
if (typeof window !== 'undefined') {
setData(makeData(10_000));
setIsLoading(false);
}
}, []);
useEffect(() => {
//scroll to the top of the table when the sorting changes
try {
rowVirtualizerInstanceRef.current?.scrollToIndex?.(0);
} catch (error) {
console.error(error);
}
}, [sorting]);
const table = useMaterialReactTable({
columns,
data, //10,000 rows
defaultDisplayColumn: { enableResizing: true },
enableBottomToolbar: false,
enableColumnResizing: true,
enableColumnVirtualization: true,
enableGlobalFilterModes: true,
enablePagination: false,
enableColumnPinning: true,
enableRowNumbers: true,
enableRowVirtualization: true,
muiTableContainerProps: { sx: { maxHeight: '600px' } },
onSortingChange: setSorting,
state: { isLoading, sorting },
rowVirtualizerInstanceRef, //optional
rowVirtualizerOptions: { overscan: 5 }, //optionally customize the row virtualizer
columnVirtualizerOptions: { overscan: 2 }, //optionally customize the column virtualizer
});
return <MaterialReactTable table={table} />;
};
export default Example;
Here I'm just restoring it back once you resize it and It's a temporary workaround until the bug is addressed officially.
import { useEffect, useMemo, useRef, useState } from 'react';
import {
MaterialReactTable,
useMaterialReactTable,
type MRT_ColumnDef,
type MRT_SortingState,
type MRT_RowVirtualizer,
} from 'material-react-table';
import { makeData, type Person } from './makeData';
const Example = () => {
const columns = useMemo<MRT_ColumnDef<Person>[]>(() => [
{ accessorKey: 'firstName', header: 'First Name', size: 150 },
{ accessorKey: 'middleName', header: 'Middle Name', size: 170 },
{ accessorKey: 'lastName', header: 'Last Name', size: 150 },
{ accessorKey: 'email', header: 'Email Address', size: 300 },
{ accessorKey: 'phoneNumber', header: 'Phone Number', size: 250 },
{ accessorKey: 'address', header: 'Address', size: 300 },
{ accessorKey: 'zipCode', header: 'Zip Code' },
{ accessorKey: 'city', header: 'City', size: 220 },
{ accessorKey: 'state', header: 'State' },
{ accessorKey: 'country', header: 'Country', size: 350 },
{ accessorKey: 'petName', header: 'Pet Name' },
{ accessorKey: 'age', header: 'Age' },
{ accessorKey: 'salary', header: 'Salary' },
{ accessorKey: 'dateOfBirth', header: 'Date of Birth' },
{ accessorKey: 'dateOfJoining', header: 'Date of Joining' },
{ accessorKey: 'isActive', header: 'Is Active' },
], []);
const rowVirtualizerInstanceRef = useRef<MRT_RowVirtualizer>(null);
const tableContainerRef = useRef<HTMLDivElement>(null);
const scrollTopRef = useRef<number>(0);
const [data, setData] = useState<Person[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [sorting, setSorting] = useState<MRT_SortingState>([]);
useEffect(() => {
if (typeof window !== 'undefined') {
setData(makeData(10_000));
setIsLoading(false);
}
}, []);
useEffect(() => {
try {
rowVirtualizerInstanceRef.current?.scrollToIndex?.(0);
} catch (error) {
console.error(error);
}
}, [sorting]);
const handleMouseDown = () => {
if (tableContainerRef.current) {
scrollTopRef.current = tableContainerRef.current.scrollTop;
}
};
const handleMouseUp = () => {
requestAnimationFrame(() => {
if (tableContainerRef.current) {
tableContainerRef.current.scrollTop = scrollTopRef.current;
}
});
};
const table = useMaterialReactTable({
columns,
data,
defaultDisplayColumn: { enableResizing: true },
enableBottomToolbar: false,
enableColumnResizing: true,
enableColumnVirtualization: true,
enableGlobalFilterModes: true,
enablePagination: false,
enableColumnPinning: true,
enableRowNumbers: true,
enableRowVirtualization: true,
muiTableContainerProps: {
sx: { maxHeight: '600px' },
ref: tableContainerRef,
onMouseDown: handleMouseDown,
onMouseUp: handleMouseUp,
},
onSortingChange: setSorting,
state: { isLoading, sorting },
rowVirtualizerInstanceRef,
rowVirtualizerOptions: { overscan: 5 },
columnVirtualizerOptions: { overscan: 2 },
});
return <MaterialReactTable table={table} />;
};
export default Example;