I am trying to make a simple component that when you press a button, will start playing a looping song which you could control the volume of using a range input.
This is my code so far:
import { useState, useEffect } from "react";
import music from "./assets/music.mp3";
function MusicPlay() {
const [volume, setVolume] = useState(20);
const testMusic = new Audio(music);
const playMusic = () => {
testMusic.play();
testMusic.volume = volume / 100;
testMusic.loop = true;
console.log("started music", testMusic, testMusic.volume);
};
useEffect(() => {
testMusic.volume = volume / 100;
console.log("changed volume", testMusic, testMusic.volume);
}, [volume]);
return (
<div className="App">
<button onClick={playMusic}>Play</button>
<input
type="range"
min="0"
max="100"
step="1"
value={volume}
onChange={(e) => {
setVolume(Number(e.target.value));
}}
/>
</div>
);
}
export default MusicPlay;
right now you can press the button to start music, and it will start at the volume you specified, but when you move the slider it doesnt update the volume
Every render of this component will create a new instance of Audio. Changing the volume will cause a rerender. This means that you lost the reference to your playing Audio quite a while ago. If you store the audio in a ref, you'll always refer to the same audio and be able to change it's volume:
import React, { useState, useEffect, useRef } from "react";
import music from "./assets/music.mp3";
function MusicPlay() {
const [volume, setVolume] = useState(20);
const testMusicRef = useRef();
React.useEffect(() => {
if (!testMusicRef.current) {
testMusicRef.current = new Audio(music);
}
}, []);
const playMusic = () => { // consider wrapping this in useCallback too
if (!testMusicRef.current) {
return;
}
testMusicRef.current.play();
testMusicRef.current.volume = volume / 100;
testMusicRef.current.loop = true;
console.log(
"started music",
testMusicRef.current,
testMusicRef.current.volume
);
};
useEffect(() => {
if (!testMusicRef.current) {
return;
}
testMusicRef.current.volume = volume / 100;
console.log(
"changed volume",
testMusicRef.current,
testMusicRef.current.volume
);
}, [volume]);
return (
<div className="App">
<button onClick={playMusic}>Play</button>
<input
type="range"
min="0"
max="100"
step="1"
value={volume}
onChange={(e) => {
setVolume(Number(e.target.value));
}}
/>
</div>
);
}
export default MusicPlay;