I have tried to set up an event listener which would listen to the user's click or keydown inputs to display the associated clip name.
const audioClips = [
{
keyCode: 81,
keyTrigger: "Q",
id: "Heater 1",
src: "https://s3.amazonaws.com/freecodecamp/drums/Heater-1.mp3"
},
{
keyCode: 67,
keyTrigger: 'C',
id: 'Snare',
link: 'https://s3.amazonaws.com/freecodecamp/drums/Brk_Snr.mp3'
}
]
function App() {
const [displayClipName, setDisplayClipName] = React.useState('Click a key!')
React.useEffect(() => {
document.addEventListener('keydown', (e) => {
const keyDownUpperCase = e.key.toUpperCase()
changeDisplay(keyDownUpperCase)
});
return () => {
document.removeEventListener('keydown', changeDisplay)
}
}, [])
const handleCLick = (e) => {
const targetId = e.target.id.slice(e.target.id.length -1, e.target.id.length);
console.log('here')
changeDisplay(targetId)
}
const changeDisplay = (keyDownUpperCase, targetId) => {
console.log(keyDownUpperCase)
console.log(targetId)
if (keyDownUpperCase || targetId === 'Q') {
setDisplayClipName('You played: ' audioClips[0].id)
}
else if (keyDownUpperCase || targetId === 'C') {
setDisplayClipName('You played: ' audioClips[8].id)
}
else {
setDisplayClipName('You missed it, try again!')
}
}
return (
<div id="drum-machine" className="text-white text-center">
<div id="display" className="container-fluid bg-info rounded p-2">
<h1>FCC - Drum Machine</h1>
<div onClick={handleCLick} className="pad-container bg-warning rounded p-3 m-3">
{audioClips.map((clip) => (
<Pad
key={clip.id}
clip={clip}
/>
))}
</div>
<h2>{displayClipName}</h2>
</div>
</div>
)
}
My app looks like this but apparently, I am missing something because the third console.log(targetID) returns an empty string, and the displayClipName
does only listen the first keydown input.
Any insights on that's wrong is more than welcome!
CodePudding user response:
Try adding an id attribute to the div. The div with the onClick handler needs to have an id
attribute, for example:
<div id="example-id" onClick={handleCLick} className="pad-container bg-warning rounded p-3 m-3">
e.target.id
returns the value of the id attribute of the tag the event was triggered against.
CodePudding user response:
When you add your eventListener you only provide the the key which is pressed. You also provide an anonymous function which can't be used to remove the event in the useEffect cleanup.
React.useEffect(() => {
document.addEventListener("keydown", (e) => {
const keyDownUpperCase = e.key.toUpperCase();
changeDisplay(keyDownUpperCase);
});
return () => {
document.removeEventListener("keydown", changeDisplay);
};
}, []);
To fix the issue of the eventlisteners we can just use the changeDisplay
function as a handler.
React.useEffect(() => {
document.addEventListener("keydown", changeDisplay);
return () => {
document.removeEventListener("keydown", changeDisplay);
};
}, []);
Then in the changeDisplay
function we need some adjustments. Your changeDisplay
takes in 2 arguments. One for an event and another for some id. We can just transform the key
to upper case in the function no need to do it somewhere else. The rest of the functionality can stay the same.
const changeDisplay = (e, targetId) => {
const keyDownUpperCase = e?.key?.toUpperCase();
...
};
Then there is the handleClick
function. Which takes in a event, there is no need for that since we can simply provide the keyTrigger
when we call the function. It can even we removed if you want to.
const handleClick = (id) => {
changeDisplay(id);
};
For this example I just added a div
around the Pad
component, if its a custom component you can add a onClick
to the component instead of the extra div. We change the onClick
to call handleClick
with the first parameter of null
since we don't have a KeyboardEvent
and the second parameter the clip.keyTrigger
for the changeDisplay
to handle.
<div className="pad-container bg-warning rounded p-3 m-3">
{audioClips.map((clip) => (
<div onClick={() => handleClick(null, clip.keyTrigger)}>
<Pad key={clip.id} clip={clip} />
</div>
))}
</div>