管理大量记录时setState长执行

时间:2019-06-26 16:35:26

标签: javascript reactjs

我正在尝试解决React应用程序中发生的问题。在其中一个视图(组件)中,我有一个可处理大数据的管理工具。基本上,当视图加载时,我会触发componentDidMount来触发ajax提取,该提取将下载由大约50.000条记录组成的数组。每个数组行都是一个具有8-10个键值对的对象。

import React, { Component } from "react";
import { List } from "react-virtualized";
import Select from "react-select";

class Market extends Component {
  state = {
    sports: [], // ~ 100 items
    settlements: [], // ~ 50k items
    selected: {
      sport: null,
      settlement: null
    }
  };

  componentDidMount() {
    this.getSports();
    this.getSettlements();
  }

  getSports = async () => {
    let response = await Ajax.get(API.sports);

    if (response === undefined) {
      return false;
    }

    this.setState({ sports: response.data });
  };

  getSettlements = async () => {
    let response = await Ajax.get(API.settlements);

    if (response === undefined) {
      return false;
    }

    this.setState({ settlements: response.data });
  };

  save = (key, option) => {
    let selected = { ...this.state.selected };
    selected[key] = option;
    this.setState({ selected });
  };

  virtualizedMenu = props => {
    const rows = props.children;
    const rowRenderer = ({ key, index, isScrolling, isVisible, style }) => (
      <div key={key} style={style}>
        {rows[index]}
      </div>
    );

    return (
      <List
        style={{ width: "100%" }}
        width={300}
        height={300}
        rowHeight={30}
        rowCount={rows.length || 1}
        rowRenderer={rowRenderer}
      />
    );
  };

  render() {
    const MenuList = this.virtualizedMenu;

    return (
      <div>
        <Select
          value={this.state.selected.sport}
          options={this.state.sports.map(option => {
            return {
              value: option.id,
              label: option.name
            };
          })}
          onChange={option => this.save("sport", option)}
        />
        <Select
          components={{ MenuList }}
          value={this.state.selected.settlement}
          options={this.state.settlements.map(option => {
            return {
              value: option.id,
              label: option.name
            };
          })}
          onChange={option => this.save("settlement", option)}
        />
      </div>
    );
  }
}

我遇到的问题是,在下载大数据并将其保存为查看状态之后,即使我要使用具有〜100条记录的select来更新值,也要花几秒钟的时间。例如,假设smallData是100个项目的数组,而{ id: n, name: 'xyz' }selectedFromSmallData只是数据数组中的单个项目,使用html select进行选择。

在大数据加载之前进行选择需要花费ms,但是在数据加载并保存到状态后,突然需要2-4秒。

可能会帮助解决该问题的方法(不幸的是,我无法分页该数据,而我无法访问其任何内容)。

2 个答案:

答案 0 :(得分:1)

.map()在每个渲染器上创建一个新数组。为避免这种情况,您有以下三种选择:

  1. 存储state.sportsstate.settlements已为Select准备的
  2. 每次更改state.sportsstate.settlements时也会更改state.sportsOptionsstate.settlementsOptions
  3. 使用componentDidUpdate更新state.*Options

第三个选项可能更易于实现。但这会触发其他重新渲染:

  componentDidUpdate(prevProps, prevState) {
    if (prevState.sports !== this.state.sports) {
      this.setState(oldState => ({sportsOptions: oldState.sports.map(...)}));
    }
    ...
  }

您的onChange处理程序将在每次渲染时重新创建,并可能触发Select的不必要的重新渲染。创建两个单独的方法来避免这种情况:

saveSports = option => this.save("sport", option)
...
render() {
  ...
  <Select onChange={this.saveSports}/>
  ...
}

您对components={{ MenuList }}有类似的问题。将其移至状态或构造函数,这样{MenuList}对象仅创建一次。您应该以如下形式结束:

    <Select
      components={this.MenuList}
      value={this.state.selected.settlement}
      options={this.state.settlementsOptions}
      onChange={this.saveSettlements}
    />

如果这样做没有帮助,请考虑使用默认的select并使用PureComponent来呈现其选项。或尝试使用custom PureComponents渲染Select的一部分。

还要检查React-select is slow when you have more than 1000 items

答案 1 :(得分:0)

数组的大小应该不成问题,因为只有引用存储在状态对象中,而react不会对状态做任何深度相等。 也许您的render或componentDidUpdate在这个大数组上进行迭代,这会导致问题。 如果这样做没有帮助,请尝试配置您的应用。