I can't pass the 5th and 6th test, although the app working

Tell us what’s happening:
the app working fine on click I can here the sounds. Also, if I click the keyboard its working. Unfortunately, it’s not passing tests. I wish someone could help me.

Your code so far
here is the code so far fro every pad:

const African = ({playAudio}) => {

    {useKeyPresss("Q") && playAudio(african)}

    return ( 
        <button 
                id="african" 
                className="drum-pad"
                onClick={() => playAudio(african)}
            >Q<audio id="Q" src={african} className="clip"></audio>
         </button>
     );
}
 

The components whose holding my all pads:

const Pads = () => {

    const playAudio = (src) => {
        let audio = new Audio(src)
        audio.play()
    };
    
    return ( 
        <div className="drum-pads">
            <African playAudio={playAudio} />
            <Bassbell playAudio={playAudio} />
            <Bling playAudio={playAudio} />
            <Clap playAudio={playAudio} />
            <Ccuriouscym1 playAudio={playAudio} />
            <Kick playAudio={playAudio} />
            <Dramatic playAudio={playAudio} />
            <Snare playAudio={playAudio} />
            <Timbale playAudio={playAudio} />
        </div>
     );
}

for the keypress i’m using a custom hook useKeyPress :

function useKeyPress(targetKey) {
  // State for keeping track of whether key is pressed
  const [keyPressed, setKeyPressed] = useState(false);
  // If pressed key is our target key then set to true
  function downHandler({ key }) {
    if (key === targetKey) {
      setKeyPressed(true);
    }
  }
  // If released key is our target key then set to false
  const upHandler = ({ key }) => {
    if (key === targetKey) {
      setKeyPressed(false);
    }
  };
  // Add event listeners
  useEffect(() => {
    window.addEventListener("keydown", downHandler);
    window.addEventListener("keyup", upHandler);
    // Remove event listeners on cleanup
    return () => {
      window.removeEventListener("keydown", downHandler);
      window.removeEventListener("keyup", upHandler);
    };
  }, []); // Empty array ensures that effect is only run on mount and unmount
  return keyPressed;
}

The link to my app to check how it is working: https://redapy.github.io/drum-machine/

Your browser information:

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

Challenge: Build a Drum Machine

Link to the challenge:

Some thoughts:

  • You have a typo here (useKeyPresss)
{useKeyPresss("Q") && playAudio(african)}
  • You have two handlers for keydown and keyup, which fire quite fast when the tests “click” on a drumpad. I’d remove the keyup handler altogether. The fact that you almost instantly set the state from keyPressed back to false could mess things up. Instead, I’d set a timeout of maybe 300ms after a keydown event.

  • In your playAudio function, you create a new audio element each time, but the audio that should be playing is the one with the id="Q" etc inside your African/Bassbell… components

  • You’re not displaying the name of the current clip in the display, it just says “display”

Hope some of that helps to fix it.

thanks for your time.
I’ve corrected the typo.

If I remove it the it gonna works twice and it won’t fire again(I don’t know the reason). I added a timeout but even that still not passing the tests.

for this idk how I can trigger the audio without creating a new Audio.

I haven’t add the display yet for test 7. I’m just trying to fix those tests first before I move on to the last test

If you remove the keyup, you need something else to set the state of keyPressed back to false (that’s why I recommended using a setTimeout with 300 milliseconds). Because if you don’t set it back to false, its state will always be true after the first keypress, and if its state doesn’t change, it won’t re-render and play the audio again.

I’ve looked at the tests, and they trigger all three possible events (keyDown, keyPress and keyUp) at the same time, so having more than one listener for key events is likely going to cause trouble with the tests.

I could think of two options here. The easier one (if you’re more familiar with Vanilla JS DOM manipulation) is to use querySelector in your play function to grab the audio element. Instead of passing in the src, you pass in the id of the audio element.

Direct DOM manipulation like that, however, is kind of frowned up in a React App. It won’t make the tests break, but if you want to do it the React way, you’d need the useRef hook. It lets you create a reference to each audio element, so you can access it directly without Vanilla’s querySelector.

I’ve tried to do so. But unfortunately, it’s not working as it should(if you pressed the keys so fast) and even that the test still not passing.

I used the useRef to grap the audio element but how I would play it ? I’ve tried like this but it gave me an error.

import React, { useState, useRef, compenents} from "react"
import African from "./African"


const Pads = () => {


    const audioRef = useRef()    

    const playAudio = (audioRef) => {
        audioRef.play()

    };
    
    return ( 
        <div className="drum-pads">
            <African audioRef={audioRef} playAudio={playAudio} />
        </div>
     );
}
 
export default Pads;

import React, { compenents, useState, useRef} from "react"
import useKeyPress from "./useKeyPress"
import african from "../pad/african.wav"

const African = ({playAudio , audioRef}) => {

    {useKeyPress("Q")  && playAudio(african)}

    return ( 
        <button 
                id="african" 
                className="drum-pad"
                onClick={() => playAudio(african)}
            >Q<audio ref={audioRef} id="Q" src={african} className="clip"></audio>
         </button>
     );
}
 
export default African;

Thanks a lot for your time and explantion. I’m new to react, and programming in general, so I know I’m doing some stupid stuff, but that’s how we learn.

I’ve tried like this also, its working. But i’m still creating new audio…

const Pads = () => {

    
    const audioRef = useRef()    

    const playAudio = (audioRef) => {
        let audio = new Audio(audioRef)
        audio.play()
    };
    
    return ( 
        <div className="drum-pads">
            <African audioRef={audioRef} playAudio={playAudio} />
            <Bassbell audioRef={audioRef} playAudio={playAudio} />
            <Bling audioRef={audioRef} playAudio={playAudio} />
            <Clap audioRef={audioRef} playAudio={playAudio} />
            <Ccuriouscym1 audioRef={audioRef} playAudio={playAudio} />
            <Kick audioRef={audioRef} playAudio={playAudio} />
            <Dramatic audioRef={audioRef} playAudio={playAudio} />
            <Snare audioRef={audioRef} playAudio={playAudio} />
            <Timbale audioRef={audioRef} playAudio={playAudio} />
        </div>
     );
}
 
export default Pads;
import React, { compenents, useState, useRef} from "react"
import useKeyPress from "./useKeyPress"
import african from "../pad/african.wav"

const African = ({playAudio , audioRef}) => {

    {useKeyPress("Q")  && playAudio(african)}

    return ( 
        <button 
                id="african" 
                className="drum-pad"
                onClick={() => playAudio(african)}
            >Q<audio ref={audioRef} id="Q" src={african} className="clip"></audio>
         </button>
     );
}
 
export default African;

First of all, nobody would kill you if you just took the easy route and make use of document.querySelector(audioId)
to grab the <audio> tag and play it.

As for useRef - in the version you posted above, you’re using it in your parent component Pad, which renders nine DrumPads, but all with the same audioRef. That cannot work, the child components themselves need to use useRef to get a reference to their audio elements. To use that reference, you have to access the .current property on it.

Here’s some example code:

const African = () => {

    const audioRef = useRef(null) // initialise with null

    useEffect(() => {
      if (keyPressed) {
        audioRef.current.play()
      }
    }, [keyPressed])

    return ( 
        <button 
            id="african" 
            className="drum-pad"
            onClick={() => playAudio(african)}
            >Q<audio ref={audioRef} id="Q" src={african} className="clip"></audio>
        </button>
     );
}

That is by no means a full working solution, but if you want to go down the useRef route, this will hopefully get you started.

tanks a lot I do understand now.

BTW I just noticed that the timeout of 300ms that I suggested is far too long, I revisited my own version of this project and a timeout of 10ms made my tests pass (while 300 made them fail). Don’t know if that helps, just wanted to mention it.

I’ll try with 10ms timeout. I’m thinking to redo all the project from scratch and to put all audio in one list of object with their id, url and all. Because I’ve just saw the example in code pen what they give us. I think it’s much easier but I didn’t think of it before. I’m thinking to use the map method to output the different pads.

This is a perfect use case for .map, and you’ll often see it in React. All the pads are essentially the same component, with the only difference being their ids/keyCodes/audio sources. React really shines when you have repetitive code blocks like that.

I just checked and even the example project uses document.getElementById to grab the audio element. I wouldn’t call it good practice, but this is a project for beginners and I guess they didn’t want to overcomplicate things with less-common hooks like useRef.

Yeah I’ve built now the template with .map it’s really useful. Ig they didn’t use the useRef hook bcz the course uses the “old react”, which use the class components and not the modern react with functional components. However I prefer the “modern react” as I found it more flexible and easier to understand. I can’t thank you enough for your help and the time you gave me.

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