You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Material React Table列宽调整后滚动位置重置问题求助

解决Material React Table调整列宽后滚动位置重置问题

这个问题源于列宽调整触发表格重渲染时,虚拟滚动器的滚动位置未被保留,导致自动回到顶部。以下是具体解决步骤:

步骤1:添加滚动位置保存的Ref

新增一个Ref用于存储当前滚动到的起始行索引:

const savedScrollIndex = useRef<number>(0);

步骤2:监听滚动事件保存位置

在rowVirtualizerOptions中添加onScroll回调,实时记录当前滚动的起始索引:

rowVirtualizerOptions: {
  overscan: 5,
  onScroll: () => {
    const virtualizer = rowVirtualizerInstanceRef.current;
    if (virtualizer) {
      savedScrollIndex.current = virtualizer.getCurrentScrollPosition().startIndex;
    }
  },
},

步骤3:监听列宽变化并恢复滚动位置

  1. 新增列宽状态管理:
const [columnSizing, setColumnSizing] = useState<MRT_ColumnSizingState>({});
  1. 在表格配置中绑定列宽变化事件和状态:
onColumnSizingChange: setColumnSizing,
state: { isLoading, sorting, columnSizing },
  1. 添加useEffect监听列宽变化,恢复滚动位置:
useEffect(() => {
  if (rowVirtualizerInstanceRef.current && savedScrollIndex.current > 0) {
    rowVirtualizerInstanceRef.current.scrollToIndex(savedScrollIndex.current);
  }
}, [columnSizing]);

完整修改后的代码

import { useEffect, useMemo, useRef, useState } from 'react';
import {
  MaterialReactTable,
  useMaterialReactTable,
  type MRT_ColumnDef,
  type MRT_SortingState,
  type MRT_RowVirtualizer,
  type MRT_ColumnSizingState,
} 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 savedScrollIndex = useRef<number>(0); // 保存滚动索引

  const [data, setData] = useState<Person[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [sorting, setSorting] = useState<MRT_SortingState>([]);
  const [columnSizing, setColumnSizing] = useState<MRT_ColumnSizingState>({}); // 列宽state

  useEffect(() => {
    if (typeof window !== 'undefined') {
      setData(makeData(10_000));
      setIsLoading(false);
    }
  }, []);

  useEffect(() => {
    // 排序变化时滚动到顶部
    try {
      rowVirtualizerInstanceRef.current?.scrollToIndex?.(0);
      savedScrollIndex.current = 0; // 重置保存的索引
    } catch (error) {
      console.error(error);
    }
  }, [sorting]);

  // 列宽变化后恢复滚动位置
  useEffect(() => {
    if (rowVirtualizerInstanceRef.current && savedScrollIndex.current > 0) {
      rowVirtualizerInstanceRef.current.scrollToIndex(savedScrollIndex.current);
    }
  }, [columnSizing]);

  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' } },
    onSortingChange: setSorting,
    onColumnSizingChange: setColumnSizing, // 监听列宽变化
    state: { isLoading, sorting, columnSizing },
    rowVirtualizerInstanceRef,
    rowVirtualizerOptions: { 
      overscan: 5,
      onScroll: () => {
        // 实时保存当前滚动起始索引
        const virtualizer = rowVirtualizerInstanceRef.current;
        if (virtualizer) {
          savedScrollIndex.current = virtualizer.getCurrentScrollPosition().startIndex;
        }
      },
    },
    columnVirtualizerOptions: { overscan: 2 },
  });

  return <MaterialReactTable table={table} />;
};

export default Example;

原理说明

  • 通过虚拟滚动器的onScroll事件实时记录当前滚动的起始行索引
  • 监听列宽变化状态,在列宽调整完成后调用scrollToIndex恢复之前的滚动位置
  • 保留了原有排序后滚动到顶部的逻辑,同时避免列宽调整时干扰滚动位置

内容的提问来源于stack exchange,提问作者Maria Avanova

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 03:57:16