【问题标题】:React Virtualized Table - performance issues with input element in ~8 columns tableReact Virtualized Table - 约 8 列表中的输入元素的性能问题
【发布时间】:2019-12-10 06:24:10
【问题描述】:

我正在尝试创建一个 8 列表,每列包含 <input /> 元素。出于某种原因,我在文本输入的 on change 事件中遇到了延迟。将列数减少到 4 以下会使体验更好。这可能有点道理,但我也尝试增加列的数量,我发现对于 10 列或更多列,体验再次很棒。您可以查看我的超级简单的 React 应用程序,您可以在其中动态更改列数 - http://com.react.table-with-inputs.s3-website.eu-central-1.amazonaws.com/

这是我的代码:

import React from 'react';
import { AutoSizer, Table, Column } from 'react-virtualized';

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      numOfCols: '8',
    };
  }

  rowGetter = ({ index }) => {
    return { index };
  };

  renderHeaderCell = ({ label }) => {
    return (
      <span>{label}</span>
    );
  };

  renderCell = ({ rowIndex, columnIndex }) => {
    return (
      <input key={`input-${rowIndex}-${columnIndex}`} />
    );
  };

  render() {
    return (
      <div style={{ display: 'flex', flex: 1, flexDirection: 'column', padding: '10px' }}>
        <span>NUMBER OF COLUMNS</span>
        <input
          value={this.state.numOfCols}
          onChange={e => this.setState({ numOfCols: e.target.value })}
        />
        <br />
        <br />
        <AutoSizer style={{ flex: 1 }}>
          {({ height, width }) => (
            <Table
              width={width}
              height={height}
              estimatedRowSize={36}
              overscanRowCount={10}
              headerHeight={30}
              rowHeight={36}
              rowCount={20}
              rowGetter={this.rowGetter}
            >
              {
                (() => {
                  const cols = [];

                  for (let i = 0; i < parseInt(this.state.numOfCols); i += 1) {
                    cols.push(
                      <Column
                        label={`Title${i + 1}`}
                        dataKey={`title${i + 1}`}
                        width={120}
                        cellRenderer={this.renderCell}
                        headerRenderer={this.renderHeaderCell}
                      />
                    )
                  }

                  return cols;
                })()
              }
            </Table>
          )}
        </AutoSizer>
      </div>
    );
  }
}

export default App;

编辑 1: 这仅在 Chrome(Mac 和 Windows、桌面和移动设备)上发生。它不会在 Safari 或 Firefox 上重现(不是桌面版,也不是移动版)。

编辑 2: 我试图删除AutoSizer,但没有任何改变。上传了一个更新版本,该版本能够渲染或不渲染 TableAutoSizer 容器 (http://com.react.table-with-inputs.s3-website.eu-central-1.amazonaws.com/)。

【问题讨论】:

  • 对我来说,即使 20 列也可以正常工作?
  • 我刚刚尝试了您的链接,并快速输入了从 1 到 25 的所有数字。我没有遇到任何渲染延迟。造成问题的环境肯定有其他原因。
  • @TarunLalwani 20 cols 对我来说也很好用,但是在 5 到 9 cols 的情况下,我会延迟输入元素的更改。
  • @MichaelCheng 我在多个操作系统、浏览器等上进行了尝试。您是否将输入一个输入(当您有 8 个列时)与不同网站上的“常规”输入(例如 SO 中的评论框 :) 进行比较?
  • 对我来说适用于任意数量的列。您可以运行开发构建并查看所有事件发生的情况吗?

标签: reactjs react-virtualized


【解决方案1】:

我没有重现该问题,但是我在您的代码中看到了多个可能在该代码中产生性能问题的点,所以这是我的尝试。

首先,您是否尝试过减少渲染次数?

例如,也许您的组件可以切换到 PureComponent 以便它仅在状态和道具更改时重新呈现。这可以提高性能。您需要进行大量测试,因为您需要检查子组件是否需要重新渲染。

删除内联样式,因为这每次都会创建一个新对象,因此破坏了 react 可以进行的每个渲染优化。如果您仍然需要 style={something} 则将 something 移动到类声明之外的 const 变量,以确保对象被创建一次。请不要认为这是一种不好的做法,因此请检查代码中出现的所有其他情况。

在那里,我看到至少有两个地方 react 会重新渲染一个元素,但该元素并没有改变,因为对象内容是硬编码的。

你为什么要创建一个新函数然后像这样内联执行它?

更有效的方法是使用 lodash 的地图:

map(range(this.state.numOfCols), i => (<Column
  label={`Title${i + 1}`}
  dataKey={`title${i + 1}`}
  width={120}
  cellRenderer={this.renderCell}
  headerRenderer={this.renderHeaderCell}
/>))

一旦所有明显的问题都得到解决,安装一个 perf 包,例如 https://github.com/welldone-software/why-did-you-render,并检查为什么你的 App 组件渲染过多。

也许您的示例中未包含某些代码导致组件每次都重新呈现?

编辑:重读代码后,我 75% 确定问题出在 style={{ flex: 1 }} 导致完全 AutoSizer 重新渲染!

【讨论】:

  • 将 style={{ flex: 1 }} 移动到静态变量并没有改变任何事情。此外,不幸的是,据我理解并接受(大部分)您的 cmets,它不会影响输入问题。
猜你喜欢
  • 2016-12-03
  • 1970-01-01
  • 2017-12-22
  • 2018-02-20
  • 2017-03-23
  • 2018-04-18
  • 2017-09-15
  • 2020-04-15
  • 2021-06-17
相关资源
最近更新 更多