Ref issue in React Drum Machine

I’ve been stuck on this for days (and read a lot of topics on here related to it) so I thought I should finally ask for a bit of help. I’m trying to build the drum machine but the onKeyPress element just isn’t working, it only plays the audio from the last clicked button.

I’ve set up a ref which I thought would’ve been the solution but still no luck, so any help would be much appreciated!

class DrumMachine extends React.Component {

  constructor(props) {

    super(props);

    this.state = {

      display: "Click or a press a key to play",

    };

    this.handleDisplay = this.handleDisplay.bind(this);

  }

  handleDisplay(display) {

    this.setState({ display });

  }

  render() {

    return (

      <div id="drum-machine">

        {drumSounds.map((drums) => (

          <DrumPad

            key={drums.id}

            name={drums.name}

            src={drums.audioLoc}

            display={this.handleDisplay}

            trigger={drums.trigger}

            code={drums.code}

          />

        ))}

        <div id="display">{this.state.display}</div>

      </div>

    );

  }

}

class DrumPad extends React.Component {

  constructor(props) {

    super(props);

    this.handleClick = this.handleClick.bind(this);

    this.handleKeyDown = this.handleKeyDown.bind(this);

    this.audio = React.createRef();

  }

  componentDidMount() {

    document.addEventListener("keydown", this.handleKeydown);

    window.focus();

  }

  componentWillUnmount() {

    document.removeEventListener("keydown", this.handleKeydown);

  }

  handleClick = (e) => {

    this.audio.current.play();

    this.audio.current.currentTime = 0;

  };

  handleKeyDown = (e) => {

    this.audio.current.play();

    this.audio.current.currentTime = 0;

  };

  render() {

    return (

      <div>

        <div className="drum-pad" id={this.props.id}>

          <button onClick={this.handleClick} onKeyPress={this.handleKeyDown}>

            {this.props.trigger}

            <audio

              src={this.props.src}

              id={this.props.name}

              ref={this.audio}

            ></audio>

          </button>

        </div>

      </div>

    );

  }

}

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36.

Challenge: Build a Drum Machine

Link to the challenge:

The onKeyPress event is only going to fire for the button that has focus and then it will always just play the same audio no matter which key you press.

Not sure using a ref is the right solution (I never really looked into it much for this challenge). I think attaching an event listener to the document and using the event.key to grab the correct audio element using a normal DOM selector is easier (the event key and the id on the audio element match except for the case).

You will likely have to move the event listener to the parent component.

1 Like

Ok thanks I’ll give that a shot then. I’d originally gone down the DOM selector route but I’d been given the impression that was bad form when using React so switched to the ref.

I’ll have a go now and see if it works

You do want to avoid using direct DOM manipulation in React but for keyboard event listeners (you already have attached an event listener to the document) using the event to query for an element so you can call play on it shouldn’t really be an issue. Your not changing the DOM just querying the DOM for an element so you can call native methods.

Using a ref is cleaner because you avoid the normal DOM boilerplate code. But I don’t think using a ref is all that different (as far as I know) it stores the DOM element on the current prop so you can get to it.

Nice, got it working! Thanks, took me way too long.

Only problem left now is by default the drum machine isn’t the focus, so I have to click on a drum first and then the keyboard events work.