1
votes

Let us suppose we have a parent component:

class ParentComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      titleState: undefined
    };
  }

  componentWillMount() {
    // import Title string from JSON in order to assign it to titleState

    this.setState({ titleState: jsonResponse["title"] });
  }

  render() {
    return (
      <div>
        <ChildComponent title={this.state.titleState} />
      </div>
    );
  }
}

class ChildComponent extends React.Component {
  render() {
    return (
      <div>
        <h>Title is: {this.props.title}</h>
      </div>
    );
  }
}

Let us suppose server response with title comes with a significant delay so that in the beginning what we see on the screen is:

Title is:

But when response comes with jsonResponse["title"] value: "Bone Collector" we see:

Title is: Bone Collector

Now the question is:

Is it guaranteed that the state change in Parent Component will cause the Child Component to be rerendered so that is is updated with new ParentComponent's state via props?

3
See the answers please and explain to those who answer why you think they are wrong. Thank you.Sinny
the answers below are only applicable only for initial state change, but later at any instance or events if the state of parent updates than child will not re-render.coderLogs

3 Answers

3
votes

React applies diffing algorithm to see difference between both trees. When a component updates, the instance stays the same, so that state is maintained across renders. React updates the props of the underlying component instance to match the new element, and calls componentWillReceiveProps() and componentWillUpdate() on the underlying instance.

In your case, since prop passed to childComponent is changed, child component will be re-rendered with new prop value. However, react will apply diffing algorithm on old rendered output of child component and new rendered output and will only update what has changed.

To satisfy your curiosity, read : https://reactjs.org/docs/reconciliation.html

1
votes

If you call setState() in componentWillMount() of the Parent then it will not re-render the Child but use that value during the initial render of the Child component and ignore the value you set initially in the parent constructor.

As per the React docs:

UNSAFE_componentWillMount() is invoked just before mounting occurs. It is called before render(), therefore calling setState() synchronously in this method will not trigger an extra rendering. Generally, we recommend using the constructor() instead for initializing state.

But if you invoke setState() asynchronously the state of the Parent will be updated and by your code the props sent to the Child component will be changed, thus triggering a re-render of the Child component.

But it is highly unadvisable to do API calls from UNSAFE_componentWillMount instead, do the API calls from componentDidMount() of your Parent component.

This simple example will help you understand this:

class Parent extends React.Component{
   constructor(props){
      super(props);
      this.state = {foo: "first"};
    }
   UNSAFE_componentWillMount(){
      this.setState({foo: "second"}); //ignore the initial state value
      setTimeout(() => this.setState({foo: "third"}), 5000); 
   }
   render(){
     return <Child foo={this.state.foo}></Child>
   }
}

class Child extends React.Component{
   constructor(props){
      super(props);
   }
  
  render(){
    console.log("Child rendered", this.props.foo);
    return <div>This is the Child</div>
  }
}
ReactDOM.render(<Parent/>, document.getElementById("root"));
<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>

In the above snippet, Child rendered first is not seen in the console, because the setState() was called in the componentWillMount() which overwrote the foo property of the state from first to second.

This is because componentWillMount() is invoked immediately before mounting occurs so there is no chance of re-render, but the initial render will use the data of the state set in the componentWillMount().

1
votes

Specifically speaking about your code snippent in the post.

Yes. It is guaranteed that the state change in Parent Component will cause the Child Component to be rerendered.

Now the question's are,

Why the child component will get update with new values?

Because, it is bind in a way that whenever values in parent change's, it will compare (The Diffing Algorithm) the new tree with old tree and identifies what has changed and patches only changed part in a tree.

<h>Title is: {this.props.title}</h>

When does it not update the child component?

Whenever you are getting props from parent and store them in child's state, and using state values as your data source, like:

state ={
   title: this.props.title
}


<h>Title is: {this.state.title}</h>

Now in this case, data source is state. Which will get set only once when child component mounts. Whenever state changes in parent component, your child component is not aware of any props changes and in result your child component will not get updated with new values.

To make this scenario work, will need a life cycle method called componentDidUpdate, where we can set new props into state and then as state changes your child component will get re-render.

componentDidUpdate(prevProps){
   if(prevProps.title !== this.props.title){
      this.setState({title: this.props.title})
   }
}