paint-brush
Common pitfall in initialising state based on props in React JSby@atul04
33,859 reads
33,859 reads

Common pitfall in initialising state based on props in React JS

by Atul Kumar2mOctober 21st, 2017
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Recently I ran into this issue while working on <a href="https://hackernoon.com/tagged/react-js" target="_blank">React JS</a> project where I was passing a part of parent <a href="https://hackernoon.com/tagged/component" target="_blank">component</a> 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 —

Company Mentioned

Mention Thumbnail
featured image - Common pitfall in initialising state based on props in React JS
Atul Kumar HackerNoon profile picture

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.