Rob
Rob

Reputation: 3489

Why doesn't this child component rerender?

I'm experimenting with ReactJS and I'm trying to understand how child component rendering is triggered. In ReactJS, if I set up an example like this:

var externalCounterVar = 10
class Counter extends React.Component {
  constructor(props){
    super(props);
    this.state = props;
  }
  render() {
    console.log('rendering counter')
    return (
       <div> {externalCounterVar} </div>
    )
  }
}

class Main extends React.Component {
  constructor(props){
    super(props);
  }
  handleClick() {
    externalCounterVar += 1;
  }
  rerender(){
    this.render();
  }
  render() {
    console.log('rendering');
    return (
      <div>
        <button onClick={this.rerender.bind(this)} />
        <Counter counter={externalCounterVar} />
      </div>
    )
  }
}

ReactDOM.render(<Main />, document.getElementById('root'));

I'm not sure I understand why when you "rerender" it calls the render method of Main but not Counter? It seems like it should call both render methods since it's rendering Main and Counter is a child of Main.

So when rerender is called, 'rendering' will print but 'rendering counter' will not.

Upvotes: 0

Views: 93

Answers (3)

cn0047
cn0047

Reputation: 17091

In this case you don't have to use rerender method, also with purpose re-render all child components you need update state with method setState. And also accordingly to this you have to "move state up".

Here my example:

class Counter extends React.Component {
    render() {
        console.log('rendering counter');
        return (<div> {this.props.counter} </div>);
    }
}
class Main extends React.Component {
    constructor(props){
        super(props);
        this.state = {counter: props.counter};
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick() {
        this.setState(prevState => ({counter: ++prevState.counter}));
    }
    render() {
        console.log('rendering');
        return (
            <div>
                <button onClick={this.handleClick} />
                <Counter counter={this.state.counter} />
            </div>
        );
    }
}
var externalCounterVar = 10;
ReactDOM.render(
    <Main counter={externalCounterVar} />,
    document.getElementById('root')
);

Upvotes: 1

Andrii Starusiev
Andrii Starusiev

Reputation: 7774

In some situations you can use this.forceUpdate() to call re-render. But, if you can not do this, do not do. https://facebook.github.io/react/docs/react-component.html#forceupdate

Upvotes: 0

Mike Lawson
Mike Lawson

Reputation: 735

It looks like you're overlooking one of the main benefits of using React, namely how state works.

  1. You never, ever need to call this.render within a React component
  2. You should never set state dynamically, ie: this.state = ...
  3. You should always use this.setState to set your state.

Rewritten, your code should look something like the following:

const externalCounterVar = 10
class Counter extends React.Component {
  render() {
    console.log('rendering counter')
    return (
       <div> {this.props.counter} </div>
    )
  }
}

class Main extends React.Component {
  state = {
    counter: externalCounterVar
  }
  handleClick() {
    this.setState({counter: this.state.counter + 1});
  }
  render() {
    console.log('rendering');
    return (
      <div>
        <button onClick={this.handleClick.bind(this)} />
        <Counter counter={this.state.counter} />
      </div>
    )
  }
}

By calling this.setState, React automatically knows it needs to rerender your component, and as a result, all child components will also be rerendered.

Hope this helps!

Upvotes: 3

Related Questions