Reputation: 104514
Using the Chromium version of Edge: 93.0.961.38
I am building a music player app in React. When the user presses a button to select which show to listen to, it will auto-play an MP3 URL by rendering an <audio>
control with the autoPlay attribute set and a child <source>
element with the specified URL.
It all works fine when transitioning from nothing playing to the first selected show. But I want it to work such that if the user actively changes which show he listens to, it will auto play the newly selected show.
I can see from the console logging that when the other show button is pressed, that the <MediaPlayer>
element definitely re-renders using the newly selected URL. I even had it render the URL in the component itself. And I can see in the F12 tools (DOM inspector) that the src
property on the <source>
element is definitely updated. But the previous show keeps playing.
The almost good enough workaround is to introduce a STOP button that re-renders with no <audio>
control whatsoever. Then pressing the button for the other show definitely plays.
In the sample code below, click "Ambient" to start the first show. Then after it starts playing, click the "Heavy Metal" button. The "Ambient" music keeps playing. Click "Stop" then press "Heavy Metal" again - you'll hear the other show.
Is there a workaround? Or should I give up on using the built-in Audio DOM controls and use new Audio
with state?
import './App.css';
import {useState, createContext, useContext, useEffect, useMemo} from 'react';
const AMBIENT = "https://www.selbie.com/samples/atmospherics.mp3";
const METAL = "https://www.selbie.com/samples/wrekage.mp3";
const MediaPlayer = (props) => {
if (props.src) {
console.log("Url selected for playback: ", props.src);
}
else {
console.log("Nothing ready to play");
}
return (props.src ? (<div><audio controls autoPlay><source src={props.src} /></audio><br/>{props.src}</div>)
: (<div>Nothing yet to play</div>)
);
};
function App() {
const [show, setShow] = useState(null);
return (
<div>
<MediaPlayer src={show}/>
<p></p>
<button onClick={()=>setShow(AMBIENT)}>{"\u25B6\uFE0F Ambient"}</button><br/>
<button onClick={()=>setShow(METAL)}>{"\u25B6\uFE0F Heavy Metal"}</button><br/>
<button onClick={()=>setShow(null)}>{"\u23F9\uFE0F Stop"}</button><br/>
</div>
);
}
export default App;
Upvotes: 0
Views: 1144
Reputation: 104514
Based on the comment from DBS, .load()
needs to be invoked on the audio element after the source is changed. This is easily accomplished with a useEffect
with some assistance with useRef
to get access to the <audio>
element.
Updated MediaPlayer component to be:
const MediaPlayer = (props) => {
const audioRef = useRef(); // ADDED THIS
if (props.src) {
console.log("Url selected for playback: ", props.src);
}
else {
console.log("Nothing ready to play");
}
// ADDED useEffect
useEffect(()=> {
console.log("useEffect invoked");
if (props.src) {
let audio = audioRef.current;
if (audio) {
audio.load();
}
}
}, [props.src]);
// ADDED ref={audioRef} as attribute
return (props.src ? (<div><audio ref={audioRef} controls autoPlay><source src={props.src} /></audio><br/>{props.src}</div>)
: (<div>Nothing yet to play</div>)
);
};
Upvotes: 1