State property is updating but browser is not

Viewed 66

I have 2 object arrays that contain different sound src urls.And I have 2 divs Q and W. Both divs are associated with the first object array , I want to make them associated with the second array everytime another div "Toggle PadBank" is onClicked, so that they produce different sounds when they are clicked.

Everytime togglePadBank is called, state is set so that buttonState points to the second array. Im console logging state and its changing but the browser is still pointing to the old object array.

Here is my codepen: https://codepen.io/freeCodeCamp/pen/MJyNMd?editors=1010 and Here is my code:

let bankOne = [{
  key:"Q",
  col:"blue",
  audioId: '1',
  url: 'https://s3.amazonaws.com/freecodecamp/drums/Chord_1.mp3'
}, {
  key:"W",
  col:"blue",
  audioId:'2',
  url: 'https://s3.amazonaws.com/freecodecamp/drums/Chord_2.mp3'
} ]

let bankTwo = [{
    key:"Q",
    col:'blue',
    audioId:'3',
    url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-1.mp3'
  }, {
    key:"W",
    col:"blue",
    audioId:"4",
    url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-2.mp3'
  } ]




class Buttons extends React.Component{
  constructor(props){
    super(props)


  }


  render(){
    return(
    <div>
      <div style={{width:'100px', height:"100px", backgroundColor:this.props.color}} id={this.props.id} onClick={this.props.handleClick}><h3>{this.props.id} </h3></div>
        <audio id={this.props.audioId}>
        <source src={this.props.urlSource}/>
        </audio>
     </div>

    )
  }

}

class MyApp extends React.Component{
  constructor(props){
    super(props)
    this.state={
      buttonState:bankOne,
      padBankCol:"blue",
      currentPadBank:"bankOne"
    }
    this.handlesClick=this.handlesClick.bind(this)
    this.handleKeyDown=this.handleKeyDown.bind(this)
    this.playSound=this.playSound.bind(this)
    this.togglePadBank=this.togglePadBank.bind(this)
  }
  handlesClick(event){
    let indx=""
    let clickId=event.target.id
    let newState=Object.assign({},this.state)
    for(let i=0;i<this.state.buttonState.length;i++){
      if(this.state.buttonState[i].key===clickId){
        let soundId1=this.state.buttonState[i].audioId
        indx=i
        newState.buttonState[i].col="red"
        this.setState(
     newState  
    ) 
        this.playSound(soundId1)
        }
      }
     setTimeout(()=>{ 
       newState.buttonState[indx].col="blue"
       this.setState(
     newState  
    )
        },1000)


    }
  handleKeyDown(event){
    let indx=""
    let keyPress=String.fromCharCode(event.keyCode)
    console.log(keyPress)
    let newState=Object.assign({},this.state)
    for(let i=0;i<this.state.buttonState.length;i++){
      if(this.state.buttonState[i].key===keyPress){ 
        let soundId1=this.state.buttonState[i].audioId
        indx=i
        newState.buttonState[i].col="red"
       this.setState(
     newState  
    )
        this.playSound(soundId1)
        }

      }

    setTimeout(()=>{  
  newState.buttonState[indx].col="blue"
       this.setState(
     newState  
    )
        },1000)

  }


    playSound(id){
    let sound=document.getElementById(id)
    sound.play()
  }

  togglePadBank(){
    if(this.state.buttonState==bankOne){
      this.setState({
        buttonState:bankTwo,
        padBankCol:"red",
       currentPadBank:"bankTwo"
      })
    console.log(this.state.buttonState)
    } 
      else if(this.state.buttonState==bankTwo){
        this.setState({
          buttonState:bankOne,
        padBankCol:"blue",
       currentPadBank:"bankOne"
         } )
        console.log(this.state.buttonState)
      }   
  }




  render(){
    document.addEventListener("keydown",this.handleKeyDown)
    return(

    <div>
        <div style={{width:'400px',height:'400px', display:'flex',flexDirection:'row',backgroundColor:'green'}}>
        <Buttons id="Q" color= {this.state.buttonState[0].col} handleClick={this.handlesClick} urlSource={this.state.buttonState[0].url} audioId={this.state.buttonState[0].audioId}/>
          <Buttons id="W" color={this.state.buttonState[1].col} handleClick={this.handlesClick} audioId={this.state.buttonState[1].audioId} urlSource={this.state.buttonState[1].url} />
          <div style={{width:"100px",height:"50px",position:"relative",backgroundColor:this.state.padBankCol,left:"70px",top:"100px"}} onClick={this.togglePadBank}>Pad Bank Toggle</div>
        </div>



     </div>
    )
  }

}

ReactDOM.render(<MyApp />, document.getElementById('root'))
1 Answers

It is indeed very strange. At this point, the code is really messy and I could not pinpoint what exactly is wrong with it. However, there is a workaround for your case.

So instead of having an audio tag always there what you can do is create one whenever you click on the div. So something like this

playSound(i){
    let doc = document.createElement('audio')
    doc.src = this.state.buttonState[i].url

    doc.play()
}

Instead of passing id in your playSound you will need to pass the index of your matched item.

This will work. But unfortunately, I am unable to tell you why your code doesn't work. I strongly think it is because of references to objects you have put in your code. But the code is simply too dirty as of now to pinpoint what is wrong with it in a limited time interval.

Here are a few things you can do to improve your code:

  1. Update states immutably. Take extra caution when using list of objects. As making a shallow copy doesn't guarantee immutability.
  2. Reuse your functions. For example, your 'onClick' and 'onKeyPress' has redundant code.
  3. Do not compare objects directly. like this.state.buttonState==bankOne this here can cause trouble. Instead, compare objects based on unique keys.

I hope it helped. Let me know if there are any other troubles.

Related