Reputation: 1833
In my app I click Play
button and audio should start playing, when it's over speech recognition should start listening, when it's over the message should be printed in console (it will be change to some other actions further).
My app also has a feature to stop recognition process if I click stop
button, for that I use useState
where false
state changes to true
.
But, I face an issue: when I try to stop listening by clicking Stop
button, the state changes to true
and it should stop here, but despite that I have if(isPlaying === false)
condition, after true
I get false
in console as last action 🤷🏻♂️ Why it happens so, and how to fix it?
const [song] = useState(typeof Audio !== 'undefined' && new Audio())
const [isPlaying, setIsPlaying] = useState(false)
function playSong() {
setIsPlaying(!isPlaying)
if (listening) {
SpeechRecognition.stopListening()
}
if (isPlaying === false) {
song.src = 'https://raw.songToPlay.mp3'
song.play()
console.log('Song is playing: ' + isPlaying)
song.addEventListener('ended', () => {
console.log('Recognition is started: ' + isPlaying)
SpeechRecognition.startListening()
})
recognition.addEventListener('audioend', () => {
console.log('Recognition is finished: ' + isPlaying)
})
} else {
console.log('When stop is clicked: ' + isPlaying)
}
}
return (
<div>
<p>Microphone: {listening ? 'on' : 'off'}</p>
<button onClick={playSong}>{isPlaying ? 'Stop' : 'Play'}</button>
<p>{transcript}</p>
</div>
)
Output:
Upvotes: 0
Views: 105
Reputation: 126
Logging the state from within playSong will log the state when the function was defined. This means that the value will be outdated, i.e. stale. This is because of a functional concept called closures. From Wikipedia “Unlike a plain function, a closure allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.”
So how should we log the current state when it is changed? By using the useEffect hook. This function will be re-run whenever any of the dependencies change.
You can also return a 'cleanup' function to remove listeners, etc.
const [isPlaying, setIsPlaying] = useState(false);
useEffect(() => {
if (!isPlaying) {
console.log("Starting to play");
// add your event listener here to wait for song to end and start speech recognition
} else {
console.log("When stop is clicked: " + isPlaying);
// stop your speech recognition here
}
return () => {
// this is a cleanup function - you can use it to remove event listeners so you don't add them twice
};
}, [isPlaying]);
return (
<div>
<button onClick={() => setIsPlaying(!isPlaying)}>
{isPlaying ? "Stop" : "Play"}
</button>
</div>
);
Upvotes: 2