Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

create simple player with HTMLAudioElement in reactjs

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", () => {});
    };
  }, []);


CodeSandbox

1 Answers

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;
like image 199
syduki Avatar answered Apr 03 '26 19:04

syduki



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!