user2456977
user2456977

Reputation: 3964

React setting state in componentWillReceiveProps - for this component and parent component - anti-pattern?

The code below works nicely in my app. It seems like an anti-pattern though:

componentWillReceiveProps(nextProps) {
    if (nextProps.conversation.length > this.props.conversation.length){
        if (this.state.hideDiv) {
            this.setState({
                hideDiv: false,
            });
        }
        this.props.incrementNotificationCount();
    }
}

Explanation:

componentWillReceiveProps checks if nextProps conversation array is larger than this.props coversation array. Meaning: I received a new conversation message.

If I did receive a new message, I want to show a div only if it is hidden, In this component I have local state of hideDiv.

If hidden:

if (this.state.hideDiv) 

Show it:

this.setState({ 
    hideDiv: false,
}). 

Also, if I did receive a new message, I want to increment the notification count which is in a parent component:

this.props.incrementNotificationCount()

This method causes the notification state to be changed in the parent component.

Question:

I know setting local state in componentWillReceiveProps will not cause a rerender. But what happens if I set the state of a parent component in componentWillReceiveProps?

And is this code an "anti-pattern"?

Upvotes: 0

Views: 1303

Answers (1)

Shubham Khatri
Shubham Khatri

Reputation: 282130

Whenever the parent component rerenders, componentWillReceiveProps in child is called before the child render function is called. In this function if you call setState for the childComponent, the next state is updated in the componentWillUpdate function and then received by the render in the child.

Also when you call the parent function in the componentWillReceiveProps which updates the parent state, the parent is rerendered and thus the child rerenders again.

See a sample snippet which demostrates the same behaviour

class Parent extends React.Component {
    state = {
      childState: 1,
      mystate: 2,
    }
    changeState = () => {
        console.log('change state in parent called');
        this.setState((prevState) => ({
            mystate: prevState.mystate + 1
        }))
    }
    componentWillUpdate(nextProps, nextState) {
        console.log('cwu parent', nextState);
    }
    changeChildState = () => {
       console.log('change child state called');
       this.setState((prevState) => ({
           childState: prevState.childState + 1
           })
       )
    }
    render() {
        console.log('Parent rerender called');
        return (
           <div>
             <div> Parent {this.state.mystate}</div>
             <button onClick={() => this.changeChildState()}>Click</button>
             <MyComponent val={this.state.childState} changeState={this.changeState}/> 
           </div>
        )
    }
    
   
}

class MyComponent extends React.Component {
        state = {
            someChildState: 1
        }
        componentWillReceiveProps(nextProps) {
            if(nextProps.val !== this.props.val) {
               console.log('child cwrp called');
              this.setState((prevState) => ({someChildState: prevState.someChildState + 1}));
               this.props.changeState();
            }
        }
        componentWillUpdate(nextProps, nextState) {
           console.log(nextState)
        }
        render() {
           console.log('child render called')
           return <div > {this.props.val} {this.state.someChildState} </div>
        }
    }
    
ReactDOM.render(<Parent/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Upvotes: 1

Related Questions