I have created the following hook to play audio in react:
"use client";
import { useEffect, useState } from "react";
export const
useAudio = (url: string) => {
const [audio, setAudio] = 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());
}, [audio]);
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?
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.