0
votes

So here’s my code that I am working on. This is for the drum machine project. I have successfully created the buttons and the audio file is played when I click them. I am not able to pass test no 5 in the compiler which says:

"When I click on a .drum-pad element, the audio clip contained in its child element should be triggered."

Here’s the code:

import React from "react";
import ReactDom from "react-dom";
import ReactPlayer from "react-player";

const sounds = [
  {
    idnum: "1",
    id: "Q",
    src: "https://s3.amazonaws.com/freecodecamp/drums/Heater-1.mp3",
  },
  {
    idnum: "2",
    id: "W",
    src: "https://s3.amazonaws.com/freecodecamp/drums/Heater-2.mp3",
  },
  {
    idnum: "3",
    id: "E",
    src: "https://s3.amazonaws.com/freecodecamp/drums/Heater-3.mp3",
  },
  {
    idnum: "4",
    id: "A",
    src: "https://s3.amazonaws.com/freecodecamp/drums/Heater-4_1.mp3",
  },
  {
    idnum: "5",
    id: "S",
    src: "https://s3.amazonaws.com/freecodecamp/drums/Heater-6.mp3",
  },
  {
    idnum: "6",
    id: "D",
    src: "https://s3.amazonaws.com/freecodecamp/drums/Dsc_Oh.mp3",
  },
  {
    idnum: "7",
    id: "Z",
    src: "https://s3.amazonaws.com/freecodecamp/drums/Kick_n_Hat.mp3",
  },
  {
    idnum: "8",
    id: "X",
    src: "https://s3.amazonaws.com/freecodecamp/drums/RP4_KICK_1.mp3",
  },
  {
    idnum: "9",
    id: "C",
    src: "https://s3.amazonaws.com/freecodecamp/drums/Cev_H2.mp3",
  },
];

class Button extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      audioSource: "not clicked",
    };

    this.soundOn = this.soundOn.bind(this);
  }
  soundOn = (info) => {
    var audio = new Audio(info.src);
    audio.play();
  };


  render() {
    const buttonData = sounds.map((info) => (
      <button
        className="drum-pad"
        id={info["idnum"]}
        onClick={() => this.soundOn(info)}
      >
        {info["id"]}
        <audio
          src={info.src}
          className="clip"
          id={info.id}
          type="audio/mp3"
        ></audio>
      </button>
    ));
    return buttonData;
  }
}

export default Button;

Any help would be appreciated.

Thanks!

1
Please create a complete working example. You can use codesandbox.io for that. - Dekel
Nothin there. Make sure you saved the code (try to open the link in a new incognito window) - Dekel
@Dekel Done. It was taking some time to update. It should be there now. - Pruthvi
Where do you get this error? Is this some homework that you need to solve? - Dekel

1 Answers

1
votes

Instead of generating a new instance of an Audio object, you need to get a reference to the actual dom object containing the audio and play it from there.

// Using plain javascript
soundOn = (info) => {
  // Trigger the actual play method from the dom element
  // https://www.w3schools.com/tags/ref_av_dom.asp
  var audio = document.getElementById(info.id);
  audio.play();
};

You could be a little more react-idiomatic by using refs, but i'd suggest refactoring a little bit and moving each button to a separate component.

class SoundButton extends React.Component {
  constructor(props) {
    super(props)
    // any state or other logic
    this.soundOn = this.soundOn.bind(this)
  }

  audioRef = React.createRef()

  soundOn() {
    // this.audioRef.current will store a reference to the audio node
    // that is being attached to.
    // https://reactjs.org/docs/glossary.html#refs
    this.audioRef.current.play()
  }

  render() {
    const { info } = this.props
    return (
      <button
        className="drum-pad"
        id={info["idnum"]}
        // Since we are playing the sound from the dom component
        // which already has all the sound info, we don't need 
        // to pass the info to the callback.
        onClick={this.soundOn}
      >
        {info["id"]}
        <audio
          ref={this.audioRef}
          src={info.src}
          className="clip"
          id={info.id}
          type="audio/mp3"
        />
      </button>
    )
  }
}

class Buttons extends React.Component {
  // any other logic

  render() {
    return (
      sounds.map(info => 
        <SoundButton info={info} key={info.id} />
      )
    )
  }
}

If you are using hooks, you can do the same using useRef

const SoundButton = ({info}) => {
  const audioRef = useRef()
  const soundOn = () => {
    audioRef.current.play()
  }

  return (
    <button
      className="drum-pad"
      id={info["idnum"]}
      onClick={soundOn}
    >
      {info["id"]}
      <audio
        ref={audioRef}
        src={info.src}
        className="clip"
        id={info.id}
        type="audio/mp3"
      />
    </button>
  )
}