tab切换页面让滚动条自动回到顶部展示内容的方法以及scrollTo不生效原因

460 阅读2分钟

父组件

 <Tabs
    activeKey={activeItem}
    onChange={handleChangeLog}
  >
    {tabMap.map((tabItem) => {
      return (
        <Tabs.TabPane tab={tabItem.label} key={tabItem.key}>
          <Spin spinning={loading} indicator={antIcon}>
            <div className='log-box'>
              {socketMsg.length > 0 &&
                socketMsg[0].status &&
                socketMsg[0].status === "error" && (
                  <ul>
                    {socketMsg.map((item, index) => {
                      return <li key={index}>{item.message}</li>;
                    })}
                  </ul>
                )}
              {socketMsg.length > 0 && !socketMsg[0].status && (
                <VirtualTable
                  columns={tableColumns}
                  dataSource={socketMsg}
                  scroll={{
                    y: 450,
                    x: "100vw",
                  }}
                />
              )}
            </div>
          </Spin>
        </Tabs.TabPane>
      );
    })}
  </Tabs>
## antd表格虚拟滚动子组件

```javascript
import { Table } from "antd";
import classNames from "classnames";
import ResizeObserver from "rc-resize-observer";
import React, {
  useEffect,
  useRef,
  useState,
} from "react";
import { VariableSizeGrid as Grid } from "react-window";
import "./VirtualTable.less";
const VirtualTable = (props, ref) => {
  const gridRef = useRef();
  useEffect(() => {
    if (props.dataSource.length > 0) {
      gridRef.current.scrollTo({ scrollTop: 0 });
    }
  }, [props.dataSource]);
  const { columns, scroll } = props;
  const [tableWidth, setTableWidth] = useState(0);
  const widthColumnCount = columns.filter(({ width }) => !width).length;
  const mergedColumns = columns.map((column) => {
    if (column.width) {
      return column;
    }
    return {
      ...column,
      width: Math.floor(tableWidth / widthColumnCount),
    };
  });
  const [connectObject] = useState(() => {
    const obj = {};
    Object.defineProperty(obj, "scrollLeft", {
      get: () => {
        if (gridRef.current) {
          return gridRef.current?.state?.scrollLeft;
        }
        return null;
      },
      set: (scrollLeft) => {
        if (gridRef.current) {
          gridRef.current.scrollTo({
            scrollLeft,
          });
        }
      },
    });
    return obj;
  });
  const resetVirtualGrid = () => {
    gridRef.current?.resetAfterIndices({
      columnIndex: 0,
      shouldForceUpdate: true,
    });
  };
  useEffect(() => resetVirtualGrid, [tableWidth]);
  const renderVirtualList = (rawData, { scrollbarSize, ref, onScroll }) => {
    ref.current = connectObject;
    const totalHeight = rawData.length * 32;
    return (
      <Grid
        ref={gridRef}
        className='virtual-grid'
        columnCount={mergedColumns.length}
        columnWidth={(index) => {
          const { width } = mergedColumns[index];
          return totalHeight > scroll.y && index === mergedColumns.length - 1
            ? width - scrollbarSize - 1
            : width;
        }}
        height={scroll.y}
        rowCount={rawData.length}
        rowHeight={() => 32}
        width={tableWidth}
        onScroll={({ scrollLeft }) => {
          onScroll({
            scrollLeft,
          });
        }}
      >
        {({ columnIndex, rowIndex, style }) => (
          <div
            className={classNames("virtual-table-cell", {
              "virtual-table-cell-last":
                columnIndex === mergedColumns.length - 1,
            })}
            style={style}
          >
            <pre>{rawData[rowIndex][mergedColumns[columnIndex].dataIndex]}</pre>
            {/* {rawData[rowIndex][mergedColumns[columnIndex].dataIndex]} */}
          </div>
        )}
      </Grid>
    );
  };
  return (
    <ResizeObserver
      onResize={({ width }) => {
        setTableWidth(width);
      }}
    >
      <Table
        {...props}
        className='virtual-table'
        columns={mergedColumns}
        pagination={false}
        showHeader={false}
        components={{
          body: renderVirtualList,
        }}
      />
    </ResizeObserver>
  );
};

export default VirtualTable;

不生效原因

刚开始是通过点击切换tab然后通过ref去虚拟列表子组件进行操作

  // useImperativeHandle(ref, () => ({
  //   showTopContent() {
  //     console.log(gridRef, "gridRef");
  //     // console.log(gridRef.current, "gridRef");
  //     const element = document.querySelector(".virtual-table .virtual-grid");
  //     // console.log(element, "element");
  //     element.scrollTop = 0;

  //     element.scroll({ top: 0 });
  //     // const chidelement = document.querySelector(
  //     //   ".virtual-table .virtual-grid"
  //     // );
  //     // console.log(chidelement.children[0], "chidelement");
  //     // chidelement.children[0].scrollIntoView(0, 0);
  //   },
  // }));

这样操作一直不生效是因为数据没有加载完成,导致不生效

如果你的数据是异步加载的,你可能需要在数据加载完成后再执行滚动操作。你可以在数据加载的回调函数中调用 scrollTo 方法,或者在 useEffect 钩子中添加对数据的依赖。

如果你的设置没有生效,可能是因为在数据加载完成之前就已经执行了滚动操作。你可以尝试在数据加载完成后再执行滚动操作。这通常可以通过在 useEffect 钩子中添加对数据的依赖来实现。

例如,假设你的数据是存储在 socketMsg 中的,你可以这样修改 useEffect 钩子:

useEffect(() => {
  if (socketMsg.length > 0) {
    gridRef.current.scrollTo({ scrollTop: 0 });
  }
}, [socketMsg]);

这样,每次 socketMsg 更新时,都会执行滚动操作,将滚动条设置为从顶部开始展示。

如果你的数据是异步加载的,你可能需要在数据加载完成后再执行滚动操作。你可以在数据加载的回调函数中调用 scrollTo 方法,或者在 useEffect 钩子中添加对数据的依赖。

如果你仍然遇到问题,你可能需要检查你的 react-window 版本,或者考虑使用其他方法来控制滚动条的位置,例如使用原生的 DOM API。

此文章为8月Day05学习笔记,内容来源于极客时间《重学前端》,强烈推荐该课程!