Difficulty getting audio to play in drum-machine

I’m currently working on the drum-machine project in the FreeCodeCamp Front End Libraries Projects and am running into the following error with my audio /onClick /handleClick elements:

handleClick is defined as:

class DrumPad extends React.Component{
  handleClick = () => {
    return(
      this.playSound(this.props.source)
    );
  }
  render() {
    return(

This compiles successfully in react but when I click on one of my drum pads there is no audio present and a typeError is thrown.

TypeError: this.playSound is not a function
DrumPad.handleClick
src/DrumPad.js:10
   7 | class DrumPad extends React.Component{
   8 | 
   9 |   handleClick = () => {
> 10 |     return(
  11 |       this.playSound(this.props.source)
  12 |     );
  13 |   }

I considered using the React hook use-sound to get my audio to play but I wasn’t sure if this would be a better approach. Here is the repo that my code is stored in:

Any help is appreciated, thanks.

playSound is defined in App.js and you’re trying to use it in DrumPad.js. As you can see this won’t work.
You need to pass it as prop from App to DrumPad.

handleClick is also defined in App.js, does this mean that defining handleClick in DrumPad.js will cause me problems as well?

Methods you define inside a component is only available for that component. If you want to use such method in a child component you have to pass it as a prop:

// App.js
...
  <DrumPad
    playSound={this.playSound}
...
// DrumPad.js
...
handleClick = () => {
  this.props.playSound()
...

That’s why you did this in the App constructor:

this.playSound = this.playSound.bind(this);

You are binding the playSound method to the App context, so even when it is called from inside DrumPad it will still execute as if you were calling it from App.

After making that change the drum-machine compiled successfully, but this is the error that was thrown:

TypeError: Cannot read property 'play' of null

App.playSound

src/App.js:82

  79 | }
  80 | 
  81 | playSound() {
> 82 |   document.getElementById(this.state.currentSoundId).play();
     | ^  83 | }
  84 | 
  85 | 

View compiled

DrumPad.handleClick
src/DrumPad.js:9

   6 | class DrumPad extends React.Component{
   7 | 
   8 |   handleClick = () => {
>  9 |     return(
  10 |       this.props.playSound()
  11 |     );
  12 |   }

It’s telling you that

document.getElementById(this.state.currentSoundId)

is returning null.

So you need to make sure that the currentSoundId state is set properly. I don’t see where you are updating that.

If I’m understanding correctly, my code is written such that :

playSound() {
    document.getElementById(this.state.currentSoundId).play();
  }

is not updating. Am I correct in saying that this is why it is throwing the TypeError from above?

this.state.currentSoundId is not updating. So when you call

document.getElementById(this.state.currentSoundId) it returns null.

I see you have a handleClick method defined in App which sets currentSoundId, but you aren’t actually using it.

P.S. I don’t think you actually need it in App.js because you aren’t clicking on App but rather on DrumPads. I was just pointing out that you aren’t updating currentSoundId.

Okay thanks. How would I go about updating this.state.currentSoundId? Am I not defining handleClick so that this.state.currentSoundId updates when necessary?

Let’s just focus on the click event for now. I think we’ve already established that you want to call the playSound method that is passed in from App when a click happens on a DrumPad. So if you are calling a method in App (playSound) that has access to the state in App, then I would think that you could update the state in that method.

The issue here is that you need to tell playSound what to update the state with. Right now playSound is assuming that the state is already updated when it calls play(). Remember, you can pass arguments into methods :slight_smile:

P.S. Regarding App.handleClick, yes, you were updating the state in that method but you weren’t actually using that method anywhere, you were basically just defining it. But as I said, you don’t need a click handler on App because you aren’t concerned with clicks on App but rather on DrumPads. Now keypresses, those will need to be on App.

Thank you for your help. This did not compile, but would something along these lines be on the right track in terms of updating this.state.currentSoundId?

playSound() {
    if(this.currentSoundId === '') {
      this.setState(currentSoundId: this.source)
    }
    document.getElementById(this.state.currentSoundId).play();
  }

Well, I think you want currentSoundId to update every time a new sound is played, right?

Also, I’m not a react expert, but I’m not sure if you want to rely on the state to play the sound like that. I believe that setState should be treated asynchronously — in other words, do not always expect that the state has changed immediately after calling it. So I would find another way to call play on the audio element. Again, remember that you can pass arguments into a method :slight_smile:

I’m blanking and I don’t see an obvious answer here. I’m considering using something like Object.keys to get an index for Sounds so that I can potentially update this.state.currentSoundId with a for loop. Is there an easier way to go about this?

Let’s try to step through the events:

  • Each DrumPad has a click handler (DrumPad.handleClick) on it that is called when the user clicks on it.
  • DrumPad.handleClick() is going to call the App.playSound method that was passed down from App in order to play the sound.
  • App.playSound() has access to the App state, so it can update the state as well.
  • But App.playSound() doesn’t know what sound to play or what value to set currentSoundId to on its own, it has to be told.
  • DrumPad.handleClick needs to tell it by passing App.playSound() the information it needs to update the state and play the sound.

I hope this is enough for you to go on. If you get App.playSound working correctly for the click event then it will be real easy to get it working for the keypress event as well.

I believe I have fixed this error. I’m now running into the following error:

SyntaxError: Failed to execute 'querySelector' on 'Document': '#https://s3.amazonaws.com/freecodecamp/drums/Heater-1.mp3' is not a valid selector.
DrumPad.handleClick
src/DrumPad.js:9
   6 | class DrumPad extends React.Component{
   7 | 
   8 |   handleClick = () => {
>  9 |   const sound = document.querySelector(`#${this.props.src}`)
  10 |   sound.play()
  11 | }
  12 | 

Here is my repo with the updates to my code that I have made:

1 Like

querySelector() takes a valid selector as an argument. What does this.props.src contain? If you don’t know, add a

console.log(this.props.src)

at the very beginning of the method.

Actually, you shouldn’t need to do a console.log, the error message is telling you exactly what the value of this.props.src is:

SyntaxError: Failed to execute 'querySelector' on 'Document': '#https://s3.amazonaws.com/freecodecamp/drums/Heater-1.mp3' is not a valid selector.

In other words, this.props.src = https://s3.amazonaws.com/freecodecamp/drums/Heater-1.mp3, which is not a valid selector.

It looks like you want to use the id for the <audio> in the call to querySelector, so you need to pass in the actual id of the audio element. I wonder if you have that somewhere?

1 Like

(post withdrawn by author, will be automatically deleted in 24 hours unless flagged)

1 Like

Hey, just a word of advice, they frown upon opening multiple discussions for the same topic. Since you are already asking this in the other thread I think you should probably close this one.

1 Like

I have tried using the id for <audio> but I get the following error:

TypeError: Cannot read property 'play' of null
DrumPad.handleClick
src/DrumPad.js:10
   7 | 
   8 |   handleClick = () => {
   9 |   const sound = document.querySelector(`#${this.props.name}`)
> 10 |   sound.play()
  11 | }
  12 | 
  13 |   render() {

Using the <audio> id does not resolve the previous error. I believe that using src is the correct process. Is there another method that can be used to fix the syntax error in question?

Thanks.

1 Like