I am create web application a simple player.
I want to get the current time of the music that is playing.
this is log in useEffect
and is great.
React.useEffect(() => { Audio.addEventListener("timeupdate", (e) => { //setCurrentTime(e.target.currentTime); console.log(e.target.currentTime); }); return () => { Audio.removeEventListener("timeupdate", () => {}); }; }, []);
problem
when set e.target.currentTime
in state
.Music playback stops.
React.useEffect(() => { Audio.addEventListener("timeupdate", (e) => { setCurrentTime(e.target.currentTime); console.log(e.target.currentTime); }); return () => { Audio.removeEventListener("timeupdate", () => {}); }; }, []);
Advertisement
Answer
it will work with a local Audio
element:
import React from "react"; import Musics from "../data/Musics"; import Next from "../assets/next.svg"; import Prev from "../assets/prev.svg"; import Play from "../assets/play.svg"; import Pause from "../assets/pause.svg"; import Progress from "../components/Progress"; import MusicTitle from "../components/MusicTitle"; import Button from "../components/Button"; import Container from "../components/Container"; //import Audio from "../models/Audio"; const MusicPlayer = () => { const [state, setState] = React.useState({ isPlaying: false, title: Musics[0].name, path: Musics[0].path /** * Define your state here */ }); const [currentTime, setCurrentTime] = React.useState(0); const audio = new Audio(); audio.src = state.path; audio.addEventListener("timeupdate", function () { let time = this.currentTime; setCurrentTime(time); console.log(time); }); const next = () => { // TODO Implement this function }; const prev = () => { // TODO Implement this function }; const play = () => { console.log(5); audio.play(); }; return ( <Container> <MusicTitle title={state.title} /> <Progress currentTime={currentTime} /> <div className="row justify-content-center"> <Button data-testid="prev-btn" onClick={prev} src={Prev} /> <Button data-testid={state.isPlaying ? "pause" : "play-btn"} onClick={play} src={state.isPlaying ? Pause : Play} /> <Button data-testid="next-btn" onClick={next} src={Next} /> </div> </Container> ); }; export default MusicPlayer;
EDIT: to answer to OP why it doesn’t work in the initial variant
the application contains a foreign element, not controlled by React, so it should be kept in sync manually. Here, when calling setCurrentTime
the component is reevaluated, so the reassignment of Audio.src
should be prevented to not reset the audio. Also the event listener should be remover correctly to prevent memory leaks. (in fact while the code with a local Audio
element is working, it is not perfect, and pressing on play
during playback new Audio
elements will be created and will play concomitantly). Below are the changes for a working variant with a global Audio
element.
import React from "react"; import Musics from "../data/Musics"; import Next from "../assets/next.svg"; import Prev from "../assets/prev.svg"; import Play from "../assets/play.svg"; import Pause from "../assets/pause.svg"; import Progress from "../components/Progress"; import MusicTitle from "../components/MusicTitle"; import Button from "../components/Button"; import Container from "../components/Container"; import Audio from "../models/Audio"; const MusicPlayer = () => { const [state, setState] = React.useState({ isPlaying: false, title: Musics[0].name, path: Musics[0].path /** * Define your state here */ }); const [currentTime, setCurrentTime] = React.useState(0); React.useEffect(() => { const timeupdate = (e) => { setCurrentTime(e.target.currentTime); console.log(e.target.currentTime); }; Audio.addEventListener("timeupdate", timeupdate); return () => { Audio.removeEventListener("timeupdate", timeupdate); }; }); if (!Audio.src) Audio.src = state.path; const next = () => { // TODO Implement this function }; const prev = () => { // TODO Implement this function }; const play = () => { console.log(5); Audio.play(); }; return ( <Container> <MusicTitle title={state.title} /> <Progress currentTime={currentTime} /> <div className="row justify-content-center"> <Button data-testid="prev-btn" onClick={prev} src={Prev} /> <Button data-testid={state.isPlaying ? "pause" : "play-btn"} onClick={play} src={state.isPlaying ? Pause : Play} /> <Button data-testid="next-btn" onClick={next} src={Next} /> </div> </Container> ); }; export default MusicPlayer;