zmxv / react-native-sound

React Native module for playing sound clips
MIT License
2.78k stars 748 forks source link

How to stop the sound from playing in a functional component #728

Open efhan-the-unorthodox opened 3 years ago

efhan-the-unorthodox commented 3 years ago

:beetle: Description

In my react native application, I am trying to make a "shrill alarm" feature where on the press of a button, an alarm can be played repeatedly. So far I have been able to play the audio file that I want. I tied the sound function to a useEffect hook in which I was hoping I would be able to start and stop the audio from playing. When I set the state which is tied to the useEffect hook to true, I am able to get the sound to play. But when I set the state to false, the sound will not stop

:beetle: What have you tried?

The solutions from the other issues don't seem to help me because they are all class components.

:beetle: Please post your code:


const [alarmStatus, setalarmStatus] = useState(false);

//The intention here is that when I change the alarmStatus to false, I expect the 
// Sound to stop playing. But it isn't in this case
useEffect(() => {
        Sound.setCategory('Alarm')
        let alarmtest = new Sound('smalarm.mp3', Sound.MAIN_BUNDLE, (error) => {
            if (error) {
                console.log('Failed to load sound', error)
            }
            else {
                if (alarmStatus) {
                    alarmtest.play((s) => {
                        if (s) {
                            console.log('Playback success')
                            alarmtest.release();
                        }
                        else {
                            console.log('Playback fail')
                        }
                    });
                }
                else{
                    alarmtest.stop();
                }
            }
        })
    }, [alarmStatus])

:bulb: Possible solution

Are you using...

Which versions are you using?

Does the problem occur on...

If your problem is happening on a device, which device?

Hannes1 commented 2 years ago

Well remember the alarmtest variable "resets" on every rerender, so when you change state to stop the alarmtest variable is a newly initiated Sound class and it can't be used to stop the old one.

You are going to have to use something like useref to initialize the Sound class, because useref doesn't reset on rerender

alexanderhodes commented 2 years ago

The idea of @Hannes1 worked for me. You just need to do the initialization of the Sound in a useRef. With that the sound variable stays the same as well during rerender

const soundRef = useRef<Sound>(
    new Sound(soundFile, soundPath, error => {
      if (error) {
        console.error("error", error);
      }
    })
  );

Later in your code you can access the sound like this:

useEffect(() => {
  // playing
  shuttleSoundRef.current.play();
  // stopping after 2 seconds
  setTimeout(() => {
    shuttleSoundRef.current.stop();
  }, 2000);
}, []);

You can use this for checking the state of alarmStatus in a useEffect