【问题标题】:Prevent unnecessary re-render of React component防止不必要的 React 组件重新渲染
【发布时间】:2018-12-02 15:07:15
【问题描述】:

我有一个 PureComponent 渲染另一个组件并实现其onClick 回调:

class ColorPicker extends React.PureComponent {
  render() {
    console.log('ColorPicker being rendered');
    const fields = this.props.colors.map((color, idx) => {
      const fieldProps = {
        key: `${idx}`,
        color,
        /*onClick: () => { // PROBLEM HERE
          this.props.colorPicked(color);
        }*/
      };
      return <ColorField { ...fieldProps}/>;
    });
    return (
      <div className="bla-picker">
        <div>{`Refresh seed: ${this.props.seed}`}</div>
        {fields}
      < /div>
    );
  }
}

这个组件有一个小问题:每当重新渲染ColorPicker 时,嵌套的ColorFields 也需要重新渲染,因为它们的onClick 属性每次都会更改。每当渲染组件时,使用 lambda 函数将创建该函数的新实例。

我通常通过将onClick 的实现移到render 方法之外来解决这个问题,例如:onClick: this.handleClick。但是,我这里不能这样做,因为onClick 处理程序需要捕获color 变量。

解决此类问题的最佳做法是什么?

这里有一个jsfiddle 试试看;作为一个 sn-p:

class ColorField extends React.PureComponent {
  render() {
    console.log('ColorField being rendered');
    const divProps = {
      className: 'bla-field',
      style: {
        backgroundColor: this.props.color
      },
      onClick: this.props.onClick
    };
    return <div { ...divProps}/>;
  }
}
class ColorPicker extends React.PureComponent {
  render() {
    console.log('ColorPicker being rendered');
    const fields = this.props.colors.map((color, idx) => {
      const fieldProps = {
        key: `${idx}`,
        color,
        /*onClick: () => { // PROBLEM HERE
          this.props.colorPicked(color);
        }*/
      };
      return <ColorField { ...fieldProps}/>;
    });
    return (
      <div className="bla-picker">
        <div>{`Refresh seed: ${this.props.seed}`}</div>
        {fields}
      < /div>
    );
  }
}

class Layout extends React.PureComponent {
  constructor(props, ctx) {
    super(props, ctx);
    this.state = {
      seed: 1
    };
  }
  render() {
    const pickerProps = {
      colors: ['#f00', '#0f0', '#00f'],
      colorPicked: (color) => {
        console.log(`Color picked: ${color}`);
      },
      seed: this.state.seed
    };
    return (
      <div>
        <div
          className="bla-button"
          onClick = {this.btnClicked}
        >
          {'Click Me'}
        </div>
        <ColorPicker { ...pickerProps} />
      </div>
    );
  }
  btnClicked = () => {
    this.setState({ seed: this.state.seed + 1 });
  };
};

ReactDOM.render( <
  Layout / > ,
  document.getElementById("react")
);
.bla-button {
  background-color: #aaa;
  padding: 8px;
  margin-bottom: 8px;
}

.bla-picker {}

.bla-field {
  width: 32px;
  height: 32px;
}
<div id="react">
</div>

<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

只要onClick 仍然被注释掉,只有ColorPicker 会在种子更改时重新渲染(参见console.log 的输出)。只要输入onClick,所有ColorFields 也会重新渲染。

【问题讨论】:

  • 对于活生生的例子(正如我上次你问这个问题时提到的那样),请使用 Stack Snippets([&lt;&gt;] 工具栏按钮),而不是像 jsFiddle 这样的场外资源。 Stack Snippets 支持 React,包括 JSX; here's how to do one.
  • 这不是同一个问题,因为现在我使用 PureComponent 而不是 SFC。我知道 stackoverflow 允许实时示例,但是,我发现尝试一些东西很乏味;你不能,除非你按下接听按钮。
  • 您觉得它乏味是没有理由将现场示例隐藏在场外。如果您认为小提琴也有用,请也包含它,而不是代替。 SO 的工作方式,您的整个问题(包括任何必要的代码)必须in 您的问题,而不仅仅是链接。两个原因:人们不应该去场外帮助你;并且链接腐烂(而且 jsFiddle 已经出现故障——事实上,就在上周遇到了麻烦),使得问题及其答案对未来的人们毫无用处。

标签: javascript reactjs lambda closures


【解决方案1】:

您可以在 ColorField 组件中实现shouldComponentUpdate,例如:

class ColorField extends React.Component {
  shouldComponentUpdate(nextProps) {
    return this.props.color !== nextProps.color;
  }

  render(){
    const { color, onClick } = this.props;
    console.log('Color re-rendered');
    return (
      <div 
        className="color"
        onClick={onClick}
        style={{ 
          backgroundColor: color,
          height: '50px',
          width: '50px',
        }} 
      />
    )
  }
}

Example here

注意,在第一个解决方案中,我们可以只使用 React.Component,因为我们自己实现了shouldComponentUpdate。

【讨论】:

  • 第二种方案有问题。在我的原始代码中,ColorPicker 将颜色传递给父组件,这是 ColorPicker 应该做的。您消除了这一点,现在 onColorClick 不需要访问 this.props。如果回调被放回,该解决方案将不起作用。
  • 只需从主父级通过所有子级将 onClick 传递给您,而不向其传递任何参数。然后在 ColorField 中传递颜色。它必须工作
  • ColorField 应该不需要将颜色传回来,因为从它的角度来看,它只是简单的点击;只有一种颜色。另一方面,ColorPicker 知道还有其他颜色,因此它想告诉调用者选择了其中的哪一个。
  • 这是您对架构的看法。我只是告诉你如何在技术上管理你的问题,以避免最少的重新渲染
  • 当然可以。但是将复杂的问题组合成更简单的问题是一种非常常见的习语。更简单的组件不应该知道他们父母的细节。 ColorPicker 可能需要告诉它的父级更多细节,ColorField 不需要知道的事情。例如,颜色的索引。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-15
  • 2021-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-26
相关资源
最近更新 更多