skip to Main Content

I have created the following hook to play audio in react:

"use client";

import { useEffect, useState } from "react";

export const 

useAudio = (url: string) => {
  const  = useState<HTMLAudioElement | null>(null);
  const [playing, setPlaying] = useState(false);

  const setVolume = (volume: number) => {
    if (audio) audio.volume = volume;
    console.log(audio);
  };

  const toggle = () => {
    setPlaying(!playing);
    console.log(audio);
  };

  useEffect(() => {
    playing ? audio?.play() : audio?.pause();
  }, [playing]);

  useEffect(() => {
    audio?.addEventListener("ended", () => audio?.play());
  }, );

  useEffect(() => {
    setAudio(new Audio(url));
    return () => {
      audio?.removeEventListener("ended", () => audio.play());
    };
  }, []);

  return { playing, toggle, setVolume };
};

When I console log the audio element inside the toggle function, I get the correct element, but in setVolume it is always null.

I set the volume in a eventListener mouseMove like this:

    document.addEventListener("mousemove", function (e) {
      let element = document.getElementById("follow");
      let left = e.pageX;
      let top = e.pageY;
      element!.style.left = left + "px";
      element!.style.top = top + "px";

      const distance = Math.sqrt(
        Math.pow(left - devPosition.current.x, 2) +
          Math.pow(top - devPosition.current.y, 2)
      );

      setVolume(1 - distance / diagonal.current);
    });

Can this happen because I call setVolume inside such a listener? If so, what can I do about that?

2

Answers


  1. You’re setting audio.volume directly, but you’ll need to use the setAudio function, try something like this:

    const setVolume = (volume: number) => {
        setAudio(p => ({ ...p, volume }));
    };
    

    Keep in mind that the console.log after the setAudio won’t show your change immediately:
    The useState set method is not reflecting a change immediately

    Login or Signup to reply.
  2. I tried your useAudio hook and I think that there is nothing wrong with the hook.

    For example a component like this works as expected and doesn’t yield any error.

    "use client";
    
    import { useEffect } from "react";
    import { useAudio } from "./audioHook"
    
    export default function Home() {
      const controls = useAudio('./horse.mp3');
    
      useEffect(() => {
        console.log(controls.playing);
      }, [controls]);
    
      const workWithAudio = () => {
        controls.toggle();
      };
    
      const changeVolume = () => {
        controls.setVolume(0.2);
      };
      
      return (
        <main>
          <button onClick={workWithAudio}>
            The toggle button
          </button>
          <button onClick={changeVolume}>
            The volume button
          </button>
        </main>
      )
    }
    

    There could be issues in the way you handle the listener. If I understand correctly (for example look here How to add a 'mousemove' event listener to a component Cursor which is moved with the cursor pointer in ReactJS?) the preferred way to add a listener in a React component is by adding it to a DOM node, like this:

    "use client";
    
    import { useAudio } from "./audioHook"
    
    export default function Home() {
      const controls = useAudio('./horse.mp3');
    
      const handleMouseMove = (e: MouseEvent) => {
        let element = document.getElementById("follow");
        let left = e.pageX;
        let top = e.pageY;
        element!.style.left = left + "px";
        element!.style.top = top + "px";
    
        // const distance = Math.sqrt(
        //   Math.pow(left - devPosition.current.x, 2) +
        //     Math.pow(top - devPosition.current.y, 2)
        // );
    
        controls.setVolume(0.5);
      }
    
      return (
        <main onMouseMove={handleMouseMove}>
          <div id="follow">TEST</div>
        </main>
      )
    }
    
    

    I don’t have access to the devPosition and diagonal variables that you use in your function, so I had to use a constant, but if you instantiate the listener in this way you are able to set the volume without issues.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search