Jan Ciołek Jan Ciołek - 3 years ago 161
React JSX Question

React doesnt rerender component when props value is being change

Can someone explain to me why in this example the Component2 doesnt change background color after Component1 updates own state?I pass background color as a value from other component's state to the component as a prop but it doesnt work as i expected.

When you click once on the bordered box the fresh rendered box should change color after "fake request" is completed.



class Component1 extends React.Component{
constructor(props){
super(props)
this.state = {
color:'red',
innerText:'Click on me!'
}
this.someAsynchronusRequest = this.someAsynchronusRequest.bind(this);
}

someAsynchronusRequest(){
setTimeout(() => {
this.setState({
color:'blue',
innerText:'requested completed,it should be blue?'
})
},1000)

}

render(){
return(
<div onClick={this.renderBox.bind(this)} className="box click">
<div>{this.state.innerText}</div>
</div>
)
}

renderBox(){
this.someAsynchronusRequest();
ReactDOM.render(<Component2 color={this.state.color}/>,document.getElementById('component2'))
}
}

class Component2 extends React.Component {
constructor(props){
super(props)
}

render(){
return(
<div style={{backgroundColor:this.props.color}} className="red box">
</div>
)
}
}

ReactDOM.render(<Component1 />, document.getElementById('component1'))

.click {
text-align:center;
border:1px solid black;
}

.box {
width:100px;
height:100px;
margin:10px;
}

<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="component1">
</div>

<div id="component2">
</div>




Answer Source

class Component1 extends React.Component{

  constructor(props){
    super(props)
    
    this.state = {
    
      color:'red',
      innerText:'Click on me!'
    
    }
    
    this.someAsynchronusRequest = this.someAsynchronusRequest.bind(this);
  }
  
  someAsynchronusRequest(){
  
    return new Promise((resolve, reject) => {
      setTimeout(() => {
    
      this.setState({
      
        color:'blue',
        innerText:'requested completed,it should be blue?'
      
      }, () => {
        resolve()
      })
    
    },1000)
    })
  
  }
  
  render(){
  
  
  
    return(
    
    <div onClick={this.renderBox.bind(this)} className="box click">
      <div>{this.state.innerText}</div>
    </div>
    
    )
  
  }
  
  renderBox(){
    
    this.someAsynchronusRequest()
      .then(resolve => {
          ReactDOM.render(<Component2 color={this.state.color}/>,document.getElementById('component2'))
       })
    

  
  
  }

}

class Component2 extends React.Component {

  constructor(props){
  
    super(props)
  
  }
  
  render(){
  
  
    return(
    
      <div style={{backgroundColor:this.props.color}} className="red box">
      
      </div>
    
    )
  
  }

}

ReactDOM.render(<Component1 />, document.getElementById('component1'))
.click {

  text-align:center;
  border:1px solid black;

}

.box {

  width:100px;
  height:100px;
  margin:10px;

}
<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="component1">

</div>

<div id="component2">

</div>

I added a promise inside the someAsynchronusRequest method. Notice that I resolve it in the callback function of setState since it is asynchronous.

The problem is that you render Component2 before the function inside timeout has been called, thus not rendering the new component with the blue color (it has not been changed yet at this point). They way you render Component2 (Using ReactDOM) is not really the idiomatic way of React. Instead you should render Component2 inside the render-function of Component1 like this. This will automatically solve the async problem and you can remove the promise:

render(){
    return(

    <div onClick={this.renderBox.bind(this)} className="box click">
      <div>{this.state.innerText}</div>
      <Component2 color={this.state.color}/>
    </div>

    )

Normally, you only want to use ReactDOM.Render to render the root-component, attaching it to some div on the page. The rest should be rendered using the render functions of your components.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download