My "KeyTar" Project Is Having Trouble With Listeners

Tell us what’s happening:
I am currently working on the fCC “Build A Drum Machine” project. I’ve decided, as I play the acoustic guitar, to make mine use chord strums instead. However I’ve run into a snag. The onscreen buttons trigger chords just fine, using onClick. But for some reason, my event listener for keydown events stops working after I trigger it more than three times, and I have no idea why.

Your code so far
Here’s my CodePen for it: https://codepen.io/roachy/pen/podmbrb

This is my listener and keydown handler:

   const keyHandler = function(event)
    {
      console.log(event.key);
      let chord = chordBank.filter(el => el.trigger == event.key)[0];
      chord.url.play();
      setChord(chord.name);
      
    }
  
   window.addEventListener('keydown', keyHandler, {once: true, capture: true});

Your browser information:

User Agent is: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.87 Safari/537.36

Challenge: Build a Drum Machine

Link to the challenge:

Any help appreciated!!

You should add the event listener inside a useEffect with an empty dependencies array, and also use the cleanup function.

React.useEffect(() => {
  window.addEventListener("keydown", keyHandler);
  return () => window.removeEventListener("keydown", keyHandler);
}, []);

I would also suggest you move the chordBank array out of the component.


BTW, you can’t pass the Drum Machine tests using the Audio constructor because the tests are looking for audio elements it can call play and pause on when testing.

1 Like

Hey lasjorg,

Thank you for your reply – sorry it took me a minute to get around to this, had to take some time away. Your useEffect suggestion is perfect.

As far as the Audio constructor… I’m gonna have to do a little more Googling :sob:

You just have to use the audio element instead. It pretty much has the same interface as the constructor, so you can use the same code to play, etc. you just have to call the methods and use the properties on the DOM element.

In React using refs is usually the best way to interact with DOM elements, but you can also use DOM methods in a pinch.

Hmmmm… So I’ve switched to the audio elements now, but I’m still failing one more test.

User Story #6: When I press the trigger key associated with each .drum-pad , the audio clip contained in its child audio element should be triggered (e.g. pressing the Q key should trigger the drum pad which contains the string Q , pressing the W key should trigger the drum pad which contains the string W , etc.).

The app seems to work fine but this test kicks up a stuff saying

Uncaught TypeError: Cannot read properties of undefined (reading ‘trigger’)

And yet when I console.log() for the variable eventKey, I get the correct result for every key… I’m really not sure what I’m missing here. It says it can’t read ‘trigger’ but I can get via console.log() just fine… any ideas?

Log out el.trigger and event.key inside the filter and compare what gets logged when you press the keys with what gets logged when the test runs.

How weird, console.log(el.trigger) is giving every single possible key… maybe I can’t use filter for this function… maybe map() instead?

EDIT: nah map has the same problem.

I’m saying to compare the strings logged when you press the key with when the test runs, you will see a difference. They are not the same (hint: casing).

const eventKey = chordBank.filter((el) => {
  console.log("el.trigger", el.trigger);
  console.log("event.key", event.key);
  return el.trigger === event.key;
});

// when you press the key it is an array with one object
// when the test runs it is an empty array
console.log(eventKey);

That is expected, you are looping the array. It is always going to go through all the elements.

Ah I see! The was testing the inputs to work with uppercase inputs too, so I just added toLowerCase() to event.key and now the tests work – plus the key inputs work if capslock is on.

Brilliant! Thank you.