skip to Main Content

I’m working on an audio player component in React, and I want to add an autostop feature to it when the user navigates to a different page. I’ve implemented the basic audio player using React hooks, but I’m not sure how to handle the autostop functionality. Here’s the code I have so far:

const AudioPlayer = ({ song }) => {
  const [isPlaying, setIsPlaying] = useState(false);
  const [play, { pause, duration, sound }] = useSound(song);
  const [seconds, setSeconds] = useState();

  const playingButton = () => {
    if (isPlaying) {
      pause(); // this will pause the audio
      setIsPlaying(false);
    } else {
      play(); // this will play the audio
      setIsPlaying(true);
    }
  };
  const [currTime, setCurrTime] = useState({
    min: "",
    sec: "",
  }); // current position of the audio in minutes and seconds

  const sec = duration / 1000;
  const min = Math.floor(sec / 60);
  const secRemain = Math.floor(sec % 60);
  const time = {
    min: min,
    sec: secRemain,
  };

  useEffect(() => {
    const interval = setInterval(() => {
      if (sound) {
        setSeconds(sound.seek([])); // setting the seconds state with the current state
        const min = Math.floor(sound.seek([]) / 60);
        const sec = Math.floor(sound.seek([]) % 60);
        setCurrTime({
          min,
          sec,
        });
      }
    }, 1000);
    return () => clearInterval(interval);
  }, [sound]);

  const audioRef = useRef(null);

  useEffect(() => {
    const handleUnload = () => {
      audioRef.current.pause();
      audioRef.current.currentTime = 0;
    };

    window.addEventListener("beforeunload", handleUnload);

    return () => {
      window.removeEventListener("beforeunload", handleUnload);
    };
  }, []);
  return (
    <div ref={audioRef} className='items-center mx-auto text-center'>
      <div>
        {!isPlaying ? (
          <button className='playButton' onClick={playingButton}>
            <IconContext.Provider value={{ size: "40px", color: "#28332B" }}>
              <AiFillPlayCircle />
            </IconContext.Provider>
          </button>
        ) : (
          <button className='playButton' onClick={playingButton}>
            <IconContext.Provider value={{ size: "40px", color: "#28332B" }}>
              <AiFillPauseCircle />
            </IconContext.Provider>
          </button>
        )}
      </div>
      <div className='flex items-center space-x-2'>
        <span className='text-[6px] font-["Helvetica_Neue"]'>
          {currTime.min}:{currTime.sec}
        </span>

        <input
          type='range'
          min='0'
          max={duration / 1000}
          default='0'
          value={seconds}
          className='accent-[#28332B] flex-1'
          onChange={(e) => {
            sound.seek([e.target.value]);
          }}
        />
        <span className='text-[6px] font-["Helvetica_Neue"]'>
          {time.min}:{time.sec}
        </span>
      </div>
    </div>
  );
};

export default AudioPlayer;

I’m using this custom Audio Player in BirdCard Component

import React from "react";
import { LazyLoadImage } from "react-lazy-load-image-component";
import "react-lazy-load-image-component/src/effects/blur.css";
import AudioPlayer from "./AudioPlayer";

const BirdCard = ({ birdImage, song, birdName }) => {
  return (
    <div className='w-full bg-[#f7f7f7] bg-opacity-60  backdrop-filter  h-[340px]'>
      <div>
        <LazyLoadImage
          effect='blur'
          className='z-50 bg-contain opacity-100 bg-inherit'
          src={birdImage}
          alt=''
          loading='lazy'
        />
      </div>
      <div>
        <h3 className="text-[20px] font-['Times_New_Roman']">{birdName}</h3>
      </div>
      <div className='px-2'>
        <AudioPlayer song={song} />
      </div>
    </div>
  );
};

export default BirdCard;

then finally this BirdCard is getting used in 14 different pages, I want to achieve whenever i move to different page, audio from previous page stops automatically.

In the above code, the audio player works fine, but I’m not sure how to trigger the autostop when the user navigates away from the page. I’ve tried using the beforeunload event, but it doesn’t seem to be working as expected.

2

Answers


  1. Chosen as BEST ANSWER

    I used this approach for above problem, and its working perfectly

    const location = useLocation();
    useEffect(() => {
    play();
    
    const handleRouteChange = () => {
      stop();
    };
    
    // Listen for the route change event
    window.addEventListener('beforeunload', handleRouteChange);
    
    return () => {
      stop();
      window.removeEventListener('beforeunload', handleRouteChange);
    };
    }, [location.pathname, play, stop]);
    

  2. If you are initialize and manage sounds inside AudioPlayer component, then the easiest solution here is to stop the sound when AudioPlayer is unmounted. In your code you have useEffect hook, that contains callback that you return from this hook. This is where the "cleanup" code should go:

    // note: you have to add stop to destructured values 
    const [play, {pause, duration, stop}] = useSound(song);
    
    // ...
    
    useEffect(() => {
      // ...
      return () => {
       stop();
       audioRef.current.pause();
       audioRef.current.currentTime = 0;
      }
    }, []);
    

    In this case you can encounter different problem that depends on the structure of your application. If your cards that contain AudioPlayer can be unmounted at some point without user changing pages (for example: in case of conditional rendering of the cards), then your sound will stop playing. If you don’t want that, then you can restructure your code so that sounds are managed at the top of your component tree and passed down to specific cards, that way you can stop sounds only if the page is unmounted.

    It looks like you are using use-sound package. If stopping sound will not work on unmounting, then it can mean, that there is something going on with cleanup logic inside the library, and you might not have control over it. If this is the case, you can always default to native html5 audio elements, that are slightly more complicated, but give you easier way to manage the sounds to prevent any memory leaks. The process is the same:

    1. add, initialize and subscribe audio at mount
    2. stop, unsubscribe and remove element at unmount
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search