2
 // 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);
  }

For every click of the button, state changes in parent component and hence ButtonRenderer.render will be called along with the child component, Button.render. Why?

I tried all 3 approaches for calling event handlers:

  1. Using inline bind(). As in the above code.

  2. Class property:

... 
increment = () => {
  this.setState({
    count: ++this.state.count
  });
}
...
 <Button handleClick={this.increment}>Click me</Button>
...
  1. Inline arrow function.
... 
increment(){
  this.setState({
    count: ++this.state.count
  });
}
...
<Button handleClick={() => {this.increment();}}>Click me</Button>
...

For every click all 3 approaches exected both the render methods.

I expected approach1 and approach2 not to call Button's render method for every click as nothing changed. I expected approach3 to call Button's render method for every click as I am using inline arrow function which will create a new function for every render for ButtonRendered class.

Browser console output for every click of the button:

Render of ButtonRenderer called.
Render of Button called

My question: Why even approach1 (using bind) and approach2(using class properties) are calling render() method of child component Button when no pros have changed to Button comp?

Arun Kandregula
  • 655
  • 3
  • 8
  • 18

3 Answers3

2

Use PureComponent if you want to avoid unneeded renders:

// 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>
Austin Greco
  • 32,997
  • 6
  • 55
  • 59
1

If a parent component is updated, does React always update all the direct children within that component?

No. React will only re-render a component if shouldComponentUpdate() returns true. By default, that method always returns true to avoid any subtle bugs for newcomers (and as William B pointed out, the DOM won't actually update unless something changed, lowering the impact). Font

Actually, by default the child components will always re render unless you explicitly tell then not to. shouldComponentUpdate should be implemented here to prevent the unecessary render unless the props value change. Or use PureComponent

Dupocas
  • 20,285
  • 6
  • 38
  • 56
0

Solution # 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>
        );
      }
    }

You Can Avoid Updates by returning false in shouldComponentUpdate method. Also you can write business login in it and return false or true depending on it.

Solution #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>
        );
      }
    }

When you extend to React.PureComponent instead of React.Component some lifecycle methods. In this Case this Works.

if you help my answer please consider to upvote

Malkhazi Dartsmelidze
  • 4,783
  • 4
  • 16
  • 40