useEffect empty dependency array inside a component which gets rendered inside useCallback has no meaning

I have a payment page, the user clicks on the button “PAY”, then a modal opens, it shows loading…, then it either renders the “payment success” component or the “payment failed” component.

When it renders any of the both, it plays a sound effect,

  • on success, it plays the sound effect of CheckTennnn! ✅ “payment success”.
  • on fail, it plays the sound effect of EEeerr.. ❌ “payment failed”.

The way I am implementing it is by having a useCallback which decides which view to render inside the modal, [success, or the fail view].

export default ProcessPaymentModal({ isSuccess, isError, isLoading }){
    const [timer, setTimer] = useState(0)
    
    useEffect(()=> { 
        const intervalId = setInterval(()=> setTimer((previousState)=> previousState + 1), 1000)
        return ()=> clearInterval(intervalId)
    })

    const View = useCallback(()=>{
      switch(true){
         case isSuccess:
           return <PaymentSuccessView timer={timer}/>
         case isError:
           return <PaymentFailView timer={timer}/>
         case isLoading:
           return <LoadingView />
      }
    }, [timer, isSuccess, isError, isLoading])

    return <View />
}

And inside these fail or success components, I have that useEffect which plays the audio only once, on mount (and it must only play the sound once).

export default function PaymentSuccessView({ timer }) {
  useEffect(() => {
    const soundEffect = new Audio('../media/checktennn.mp3')
    soundEffect.play()
  }, []);
  return <button> OK ({timer}) </button>;
}

Here’s a stack blitz instance, a code sandbox to test, (Click me)

However, the problem is that it keeps playing the sound every time the timer changes,

  • CheckTennnn! ✅
  • CheckTennnn! ✅
  • CheckTennnn! ✅

Like this, you know.

Even though I have the dependency array empty in the <PaymentSuccessView /> component, which means this useEffect function must only run once. On mount.