reactjsmaterial-react-table

Material-react table: table reloads every time after resizing columns


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;

Solution

  • 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;