Common pitfall in initialising state based on props in React JS

Written by atul04 | Published 2017/10/21
Tech Story Tags: javascript | reactjs

TLDRvia the TL;DR App

Recently I ran into this issue while working on React JS project where I was passing a part of parent component state to child component via props. Then I was using that prop to set the initial state of my child component (after doing some other calculations). So the problem was when the parent component re-renders on state change, the child component is not rendered with updated state value. For example —

// our child componentclass ChildComponent extends Component {constructor(){super(props);this.state = { count:this.props.value };}

render(){  
    return(  
        <div>  
            <p>I have {this.state.count} candies!</p>  
        </div>  
    );  
 }  

}

// In parent component

// Assuming this.state.value = 5//This will render correctly "I have 5 candies!"<ChildComponent value = {this.state.value} />

// Now parent state "value" changed to 10// This time child will again render "I have 5 candies!"<ChildComponent value = {this.state.value} />

So why is that?

Well, the constructor of the component is executed only once, when the component is mounted. It will not get executed when component re-renders. When the parent component is re-rendered it will not destroy and recreate the child component. React will reuse the child component rendered earlier and do not run the constructor.

Since we are setting the state of child component only in the constructor which does not get called when child re-renders. So the state does not update even though child component is re-rendered upon receiving new props form parent component. That’s why the state “count” in child component still have “5” as a value.

componentWillReceiveProps at rescue

From the official React documentation -

componentWillReceiveProps() is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method

nextProps is the latest props received from the parent component.

We will use this life cycle method and update the state of child component when it receives new props from the parent. We will compare the current props value with the nextProps and if they are different then we will update the state of child component using setState() method.

class ChildComponent extends Component {

constructor(){ ... }

componentWillReceiveProps(nextProps){  
    if(nextProps.value !== this.props.value){  
        this.setState({count:nextProps.value});  
    }  
}

render(){...}  

}

All though this solutions works but React recommends a different approach you might want to look at. It’s called Lifting State Up.


Published by HackerNoon on 2017/10/21