0

When I use the Chrome console to check the state of my component in the render method, I get the up-to-date state value.

However, when I display this state in a div, I get the original state value, not the updated one. Same in the React Developer Tools, the state doesn’t update there. I even tried to return true in the shouldComponentUpdate() method but it didn't help.

Here is a simplified example:

var MyComponent = React.createClass({  
  getInitialState() {
    return { myState: "value0" };
  },
  componentWillReceiveProps(nextProps){
    if(this.state.myState != nextProps.myProp){
      this.setState({
          myState: nextProps.myProp
      }); 
    }
  },
  render: function() {
    console.log("OK - up to date myState =",this.state.myState);
    return (
        <div>NOK - original state instead of updated one: {this.state.myState}</div>
    );
  }
});

ReactDOM.render(<MyComponent myProp={"myProps"} />, 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>

Would you have any explanations why the value of myState before return in the render function is correct whereas the one after return is not?

Andrew Li
  • 55,805
  • 14
  • 125
  • 143
Fred
  • 639
  • 4
  • 14
  • 27

3 Answers3

0

That's because you are initializing your state with a static value against that you must do something like this:

getInitialState() {
    return { myState: this.props.myProp };
},

See a basic example below:

class MyComponent extends React.Component { 
  constructor(props) {
    super(props)
    this.state = { myState: props.myProp }
  }
  
  componentWillReceiveProps(nextProps){
    if(this.state.myState != nextProps.myProp){
      this.setState({
          myState: nextProps.myProp
      });
    }
  }
  
  render () {
    console.log("OK ",this.state.myState)
    return (
        <div> NOK {this.state.myState} </div>
    );
  }
}

class App extends React.Component {
   constructor(props) {
      super(props)
      this.state = {
        value: 0
      }
   }
   render () {
      return <div>
       <button onClick={e => this.setState({ value: this.state.value + 1 }) }> Increase </button>
       <MyComponent myProp={this.state.value} />
       </div>
   }
}

ReactDOM.render(<App />, document.getElementById('app') )
<html>
<head>
<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>
</head>
<body>
<div id='app'></div>

</body>
</html>
damianfabian
  • 1,681
  • 12
  • 16
  • Hi @Damian, I thought passing a prop to a component initial state was an anti-pattern, are you sure this is recommended? – Fred Feb 25 '17 at 18:48
  • It was just an example, you should use props when that value won't change in your component cycle but if that data could be change it inside your component you will need to build an state based on your props and that's perfectly fine. – damianfabian Feb 25 '17 at 19:02
  • @Surreal Do you have a reference about this anti-pattern principle? A prop could be use as an initial (read-only) value your state is based on. But then...life goes on for this state :p – Remi Marenco Feb 25 '17 at 19:38
  • @remimarenco Read it there: [ReactJS: Why is passing the component initial state a prop an anti-pattern?](http://stackoverflow.com/questions/28785106/reactjs-why-is-passing-the-component-initial-state-a-prop-an-anti-pattern?rq=1) – Fred Feb 25 '17 at 19:48
  • It said don't copy props to state if your component is render base on your props. If you use your props just to initialize your state, then is ok. Basically what they are trying to avoid there is a possible problem between what is the correct value, props or state... – damianfabian Feb 25 '17 at 20:09
  • @Surreal Thanks! So as they are saying this is an anti-pattern if your goal is synchronization. But we don't know your goal here...maybe you want to create a copy of an object and `MyComponent` is a form to create that copy based on a prop? Then it makes sense the original as read-only, and the children manipulating its own state. So the question is maybe: What do you want to do? If you want to sync, the parent should be notified and manipulate itself the object (onChange in the child, function to handle that in parent) => https://facebook.github.io/react/docs/handling-events.html – Remi Marenco Feb 25 '17 at 20:38
0

I think I can see why you have troubles solving your problem.

If you want to change the value in MyComponent by changing myProp in the parent, you are on the right track using componentWillReceiveProps. Here is what you want to do:

  1. Initialize your component state with your myProp in getInitialState
  2. Use componentWillReceiveProps to change the state accordingly to the new prop passed by the parent (but then the question is: How do you manage the difference between your current state and the new prop? Maybe you don't want to reset the state if it has been modified)

Then your code should work.

I am attaching an example of how to do it:

https://jsfiddle.net/x3ef71L4/4/

Remi Marenco
  • 169
  • 4
0

Using componentWillMount() instead the constructor worked for me

Pablo Cegarra
  • 20,955
  • 12
  • 92
  • 110