Skip to content
Advertisement

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

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;
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement