【问题标题】:Prevent all children re-rendering every call of setState防止所有孩子重新渲染每次调用 setState
【发布时间】:2019-04-22 08:15:46
【问题描述】:

例如,当在表格中呈现一长串元素时,对setState 的任何调用,无论它是否更改了表格用于枚举的数据,都会导致重新呈现每个子对象。

每次选中/取消选中都会重新渲染每个元素。这会导致 2000 个更复杂的元素的严重减速。我将使用虚拟化,但在这样做之前要确保它具有尽可能高的性能。

class App extends React.Component {
  constructor(props) {
    super(props);
    this.onChange = this.onChange.bind(this);
    const rows = Array.from({ length: 200 }, (v, k) => k + 1).map(i => ({
      id: i,
      field1: "hello",
      field2: "cruel",
      field3: "world"
    }));

    this.state = {
      selectedIds: [],
      rows
    };
  }

  onChange(e) {
    const name = e.target.name;
    const checked = e.target.checked;
    const selectedIds = [...this.state.selectedIds];
    if (!checked) {
      const index = selectedIds.findIndex(x => x === name);
      selectedIds.splice(index, 1);
    } else {
      selectedIds.push(name);
    }
    this.setState(state => ({ selectedIds }));
  }

  render() {
    const { rows, selectedIds } = this.state;
    return (
      <div className="App">
        <h5>{selectedIds.length} Rows Selected</h5>
        <table>
          <thead>
            <tr>
              <td>Select</td>
            </tr>
            <tr>
              <td>Field 1</td>
            </tr>
            <tr>
              <td>Field 2</td>
            </tr>
            <tr>
              <td>Field 3</td>
            </tr>
          </thead>
          <tbody>
            {rows.map(row => {
              console.log(row);
              return (
                <tr key={row.id}>
                  <td>
                    <input
                      type="checkbox"
                      onChange={this.onChange}
                      name={row.id}
                    />
                  </td>
                  <td>
                    <div>{row.field1}</div>
                  </td>
                  <td>
                    <div>{row.field2}</div>
                  </td>
                  <td>
                    <div>{row.field3}</div>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    );
  }
}

我从其他答案中看到,重新运行 render 函数是预期的行为

ReactJS - Does render get called any time "setState" is called?

但是,如果是这样,当该链接中的元素数量增加到 4000 个时,为什么单击时会出现这么多的减速?在使用稍微复杂一些的自定义 React 组件时,如果只使用 200 个项目,就会发现速度显着下降。

【问题讨论】:

  • 让@ShawnAndrews 回答更进一步,您可能应该考虑在RowItem 组件中使用shouldComponentUpdate 向React 发出信号,表明它可能不需要根据任何道具重新渲染。 PureComponent 也可以提供帮助。此外,这也是像 recompose 和 Redux 这样的库,根据结构,也可以提供帮助。

标签: reactjs


【解决方案1】:

要解决此问题,您需要为行项目创建一个附加组件。这样它就会有一个单独的渲染函数并且不会重新渲染自己,因为行组件没有改变,只有表格组件。

{rows.map(row => {
    return (
        <RowItem row={row} onChange={this.onChange}>
    );      
}}

您的新组件将如下所示:

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

  render() {
    return (
        <tr key={this.props.row.id}>
          <td>
            <input
              type="checkbox"
              onChange={this.props.onChange}
              name={this.props.row.id}
            />
          </td>
          <td>
            <div>{this.props.row.field1}</div>
          </td>
          <td>
            <div>{this.props.row.field2}</div>
          </td>
          <td>
            <div>{this.props.row.field3}</div>
          </td>
        </tr>
    );
  }  
}

【讨论】:

  • 我想你会希望 RowItem 扩展 React.PureComponent 以便它只会在其道具发生变化时尝试渲染。没有这个,它仍然不会不必要地更新 DOM(因为它会检测到渲染结果是相同的),但它仍然会不必要地执行 RowItem 的渲染方法(并与 DOM 进行比较)。
  • 我同意 Ryan,只需更改 React.Component -> React.PureComponent 以确保您不会进行不必要的重新渲染
猜你喜欢
  • 2016-07-26
  • 2020-03-17
  • 1970-01-01
  • 2020-03-27
  • 2021-03-04
  • 2014-03-04
  • 1970-01-01
  • 2019-08-14
相关资源
最近更新 更多