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:
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:
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.
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:
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.
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
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?
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
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?
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.
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?
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.
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?