Drum Machine in React / Adding an event listener for keydown

Tell us what’s happening:
I’m building my Drum Machine, very fun exercise! But I struggled with the key press event as I didn’t know how to deal with it. I tried to add an event listener to the document, but it didn’t really worked as it was firing 2 events each time. No clue why…

Then I read a bit about it and find a post where it reads

  • FreeCodeCamp says that the componentDidMount() method is also the best place to attach any event listeners you need to add for specific functionality. In which React provides a synthetic event system which wraps the native event system present in browsers. (see here)

Since I’m using hooks for the project, I’m switching to useEffect and it works just fine, firing my event only once and enabling me to move to the next step. But I don’t get get why and it’s super frustrating.

So my questions are:

  • Why can’t I attach an event listener to the document from a component and what does it change adding it within a useEffect? Many thanks for your help and happy coding

Your code so far

import './App.css';

import {useState, useEffect} from "react";

import sound1 from "./assets/sound1.mp3";
import sound2 from "./assets/sound2.mp3";
import sound3 from "./assets/sound3.mp3";
import sound4 from "./assets/sound4.mp3";
import sound5 from "./assets/sound5.mp3";
import sound6 from "./assets/sound6.mp3";
import sound7 from "./assets/sound7.mp3";
import Pad from './components/pad';

function App() {
  // useEffect(() => {
    document.addEventListener("keydown", (e) => {console.log(e.keyCode)});
  // }, [])
  const [display, setDisplay] = useState("Play your sound");
  return (
    <div className="container">
      <div id="drum-machine">
        <div id="display">{display}</div>
        <div id="drum-pads">
          <Pad setDisplay={setDisplay} content="Q" id="Q" sound={sound1} />
          <Pad setDisplay={setDisplay} content="W" id="W" sound={sound2} />
          <Pad setDisplay={setDisplay} content="E" id="E" sound={sound3} />
          <Pad setDisplay={setDisplay} content="A" id="A" sound={sound4} />
          <Pad setDisplay={setDisplay} content="S" id="S" sound={sound5} />
          <Pad setDisplay={setDisplay} content="D" id="D" sound={sound6} />
          <Pad setDisplay={setDisplay} content="Z" id="Z" sound={sound7} />
          <Pad setDisplay={setDisplay} content="X" id="X" sound={sound7} />
          <Pad setDisplay={setDisplay} content="C" id="C" sound={sound7} />
        </div>
      </div>
    </div>
  );
}

export default App;

Your browser information:

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

Challenge: Build a Drum Machine

Link to the challenge:

If you attach event listener directly in component it’ll create a new listener for every re-render (i.e. state or props change and sometimes just because React decides that it needs to re-render). To prevent that you need to do it in useEffect with an empty dependency array. Also don’t forget to return cleanup function.

Thanks a lot @jenovs , it does make sense! I’m not super familiar with the concept of cleanup function. I googled it but not sure how to implement it here though :thinking:

useEffect(() => {
  const handler = (e) => {
    // do stuff
  }

  document.addEventListener("keydown",  handler});

  return () => {
    document.removeEventListener("keydown",  handler});
  }
}, [])
1 Like

Thanks mate, got it now! So basically when (if) the component is not existing then we clean all stuff that were added with the useEffect. Appreciate your time in helping me :pray:

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.