React Virtualized-嵌套的WindowScroller /列表

时间:2018-09-13 04:11:13

标签: javascript reactjs react-virtualized

我正在尝试使用react-virtualized渲染具有1000多个数据行的表。这些行非常重,包含多个复杂的React组件。输入,组合框,日期选择器和弹出菜单全部排在一行中。我需要整个窗口来滚动这些行。

我还需要对行进行分组并将其嵌套到显示/隐藏样式的手风琴组件中。

[+] Row Header 1
    row 1
    row 2
    ...
    row 1001
[+] Row Header 2
    row 1
    row 2
    ...
    row 1001

我不确定如何处理这种用例,或者不确定React-Virtualized是否可以处理此类事情。

我尝试过的事情:

结合使用WindowScroller / AutoSizer / List组件,并将这组由反应虚拟化的组件放入每个手风琴中。这有效,但不能解决我的问题。之所以会出现问题,是因为浏览器仍然无法处理(首次加载大约需要25秒,并且无法滚动)

我还需要使用WindowScroller / AutoSizer / List组件来处理行标题的第一级吗?

任何想法或例子将不胜感激。

2 个答案:

答案 0 :(得分:0)

您至少可以释放 UI 线程以供网络工作者滚动(当然这是一个重要的 UX 原则)。

这是一个 medium-length discussion article with an example、一个 quick implementation doc(和 the great matching article)和一个 my all-time favorite talk on the subject

这会推迟主“UI”线程的工作,但如果工作可以通过 useMemo() 钩子记忆,您也可以首先防止这种推迟。

答案 1 :(得分:0)

反应虚拟化的关键部分是 recomputeRowHeights 您可以通过此实现预期的结果。

import React, { useState, useRef, useEffect } from "react";
import {
  AutoSizer,
  Column,
  Table,
  defaultTableRowRenderer
} from "react-virtualized";

const Component = ({ list }) => {
  const [selectedIndex, setSelectedIndex] = useState(-1);
  const tableRef = useRef();

  const Details = ({ children, index }) => (
    <div style={{ cursor: "pointer" }} onClick={() => setSelectedIndex(index)}>
      {children}
    </div>
  );

  const _getDatum = index => list[index % list.length];
  const _getRowHeight = ({ index }) => (index === selectedIndex ? 96 : 48);
  const rowGetter = ({ index }) => _getDatum(index);
  const cellRenderer = ({ rowIndex }) => {
    if (rowIndex !== selectedIndex) {
      return <Details index={rowIndex}>+</Details>;
    } else {
      return <Details index={-1}>-</Details>;
    }
  };

  useEffect(
    () => {
      tableRef.current.recomputeRowHeights();
    },
    [selectedIndex]
  );

  const rowRenderer = props => {
    const { index, style, className, key, rowData } = props;
    if (index === selectedIndex) {
      return (
        <div
          style={{ ...style, display: "flex", flexDirection: "column" }}
          className={className}
          key={key}
        >
          {defaultTableRowRenderer({
            ...props,
            style: { width: style.width, height: 48 }
          })}
          <div
            style={{
              marginRight: "auto",
              marginLeft: 80,
              height: 48,
              display: "flex",
              alignItems: "center"
            }}
          >
            {rowData.details}
          </div>
        </div>
      );
    }
    return defaultTableRowRenderer(props);
  };

  return (
    <div style={{ height: "90vh" }}>
      <AutoSizer>
        {({ width, height }) => (
          <Table
            ref="Table"
            headerHeight={56}
            height={height}
            overscanRowCount={10}
            rowHeight={_getRowHeight}
            rowGetter={rowGetter}
            rowCount={1000}
            width={width}
            ref={tableRef}
            rowRenderer={rowRenderer}
          >
            <Column
              label="Index"
              cellDataGetter={({ rowData }) => rowData.length}
              cellRenderer={cellRenderer}
              dataKey="index"
              disableSort
              width={60}
            />
            <Column dataKey="name" disableSort label="Full Name" width={120} />
          </Table>
        )}
      </AutoSizer>
    </div>
  );
};

export default Component;

这是codesandbox