【问题标题】:Why child component's render is called when props did not change?为什么在 props 没有改变时调用子组件的渲染?
【发布时间】:2019-10-27 11:47:40
【问题描述】:
 // Child component
 class Button extends React.Component {
    render() {
      console.log("Render of Button called");
      return (
        <button onClick={this.props.handleClick}>
          {this.props.children}
        </button>
      );
    }
  }

 // Parent component
  class ButtonRenderer extends React.Component {
    state = {
      count: 0
    };
    increment() {
      this.setState({
        count: this.state.count + 1
      });
    }
    render() {
      console.log("Render of ButtonRenderer called.");
      return (
        <div>
          <Button handleClick={this.increment.bind(this)}>Click me</Button>
          <div>Count: {this.state.count}</div>
        </div>
      );
    }
  }

  function init() {
    var rootElement = document.getElementById("root");
    const childElement = <ButtonRenderer />;
    ReactDOM.render(childElement, rootElement);
  }

对于按钮的每次点击,父组件的状态都会发生变化,因此 ButtonRenderer.render 将与子组件 Button.render 一起被调用。 为什么?

我尝试了所有 3 种调用事件处理程序的方法:

  1. 使用内联绑定()。和上面的代码一样。

  2. 类属性:

... 
increment = () => {
  this.setState({
    count: ++this.state.count
  });
}
...
 <Button handleClick={this.increment}>Click me</Button>
...
  1. 内嵌箭头函数。
... 
increment(){
  this.setState({
    count: ++this.state.count
  });
}
...
<Button handleClick={() => {this.increment();}}>Click me</Button>
...

对于每次点击,所有 3 种方法都会执行两种渲染方法。

我希望方法 1 和方法 2 不会为每次点击调用 Button 的渲染方法,因为没有任何改变。我希望方法 3 为每次点击调用 Button 的渲染方法,因为我正在使用内联箭头函数,该函数将为 ButtonRendered 类的每个渲染创建一个新函数。

每次点击按钮的浏览器控制台输出:

Render of ButtonRenderer called.
Render of Button called

我的问题:当没有专业人士更改为 Button comp 时,为什么甚至方法 1(使用绑定)和方法 2(使用类属性)都调用子组件 Button 的 render() 方法?

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    如果父组件被更新,React 是否总是更新该组件中的所有直接子组件?

    没有。如果 shouldComponentUpdate(),React 只会重新渲染组件 返回真。默认情况下,该方法总是返回 true 以避免任何 新手的微妙错误(正如威廉 B 指出的那样,DOM 不会 除非发生变化,否则实际更新,从而降低影响)。 Font

    实际上,默认情况下,子组件将始终重新渲染,除非您明确告知不要这样做。 shouldComponentUpdate 应该在这里实现,以防止不必要的渲染,除非 props 值发生变化。或使用PureComponent

    【讨论】:

      【解决方案2】:

      如果您想避免不必要的渲染,请使用PureComponent

      // Child component
       class Button extends React.PureComponent {
          render() {
            console.log("Render of Button called");
            return (
              <button onClick={this.props.handleClick}>
                {this.props.children}
              </button>
            );
          }
        }
      
       // Parent component
        class ButtonRenderer extends React.Component {
          state = {
            count: 0
          };
          increment = () => {
            this.setState({
              count: this.state.count + 1
            });
          }
          render() {
            console.log("Render of ButtonRenderer called.");
            return (
              <div>
                <Button handleClick={this.increment}>Click me</Button>
                <div>Count: {this.state.count}</div>
              </div>
            );
          }
        }
      
        function init() {
          var rootElement = document.getElementById("root");
          const childElement = <ButtonRenderer />;
          ReactDOM.render(childElement, rootElement);
        }
        init();
      <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
      <div id="root"></div>

      【讨论】:

        【解决方案3】:

        解决方案 #1。

            // Parent component
            class ButtonRenderer extends React.Component {
              state = {
                count: 0
              };
              increment = () => {
                this.setState({
                  count: ++this.state.count
                });
              }
              render() {
                console.log("Render of ButtonRenderer called.");
                return (
                  <div>
                    <Button handleClick={this.increment}>Click me</Button>
                    <div>Count: {this.state.count}</div>
                  </div>
                );
              }
            }
        

        您可以通过在 shouldComponentUpdate 方法中返回 false 来避免更新。您也可以在其中写入业务登录并根据它返回falsetrue

        解决方案 #2。

                // Child component
            class Button extends React.PureComponent {
              constructor (props) {
                super(props)
              }
              render() {
                console.log("Render of Button called");
                return (
                  <button onClick={this.props.handleClick}>
                    {this.props.children}
                  </button>
                );
              }
            }
        
            // Parent component
            class ButtonRenderer extends React.Component {
              state = {
                count: 0
              };
              increment = () => {
                this.setState({
                  count: ++this.state.count
                });
              }
              render() {
                console.log("Render of ButtonRenderer called.");
                return (
                  <div>
                    <Button handleClick={this.increment}>Click me</Button>
                    <div>Count: {this.state.count}</div>
                  </div>
                );
              }
            }
        

        当您扩展到 React.PureComponent 而不是 React.Component 时,一些生命周期方法。在这种情况下,这是可行的。

        如果你对我的回答有帮助,请考虑投票

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-08-01
          • 1970-01-01
          • 2023-04-03
          • 2019-07-12
          • 1970-01-01
          • 1970-01-01
          • 2018-03-26
          • 2021-02-13
          相关资源
          最近更新 更多