防止不必要地重新渲染React组件

时间:2018-06-23 09:23:45

标签: javascript reactjs lambda closures

我有一个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时,嵌套的ColorField也需要重新渲染,因为它们的onClick属性会发生变化每一次。每当呈现组件时,使用lambda函数都会创建该函数的新实例。

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

解决此类问题的最佳实践是什么?

这里有jsfiddle可以尝试;并作为摘要:

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后,所有ColorField也会重新渲染。

1 个答案:

答案 0 :(得分:2)

您可以在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

请注意第一种解决方案,因为我们自己实现shouldComponentUpdate,所以只能使用React.Component。

相关问题