0

In the application, by clicking on the button, I want to do 2 things: play an audio file, show 2 pictures. After clicking the button again, I want to turn off the audio and hide 2 pictures. The audio works, but the pictures go crazy. Why? I set useState as "false" in default, but after loading pages they are "true". I do not understand why?

import {useEffect, useRef, useState} from "react";

import styled from "./Cat.module.scss";
import Swing from "../../assets/audio/GirlsLikeToSwing.mp3";

let dancingImg = ['https://i.gifer.com/P6XV.gif', 'https://i.gifer.com/DCy.gif']

const Cat = () => {
    const [isPlaying, setIsPlaying] = useState(false);
    const audioElement = useRef();
    const [ladyDancing, setLadyDancing] = useState(false);
    const [catDancing, setCatDancing] = useState(false);

    const playPause = () => {
        setIsPlaying(!isPlaying);
    }

    useEffect(() => {
        if (isPlaying) {
            audioElement.current.play();
            setLadyDancing(ladyDancing);
            setCatDancing(catDancing);
        } else {
            audioElement.current.pause();
            setLadyDancing(!ladyDancing);
            setCatDancing(!catDancing);
        }
    }, [isPlaying]);

    return (<div className={styled.headerContainer}>
            <div className={styled.lady}>
                {ladyDancing ? <img src={dancingImg[0]} alt="" className={styled.ladyDancing}/> : null}
                {catDancing ? <img src={dancingImg[1]} alt="" className={styled.catDancing}/> : null}

            </div>

            <button onClick={playPause}>Play</button>
            

            <audio src={Swing} ref={audioElement}></audio>
        </div>

    )
};
      
export default Cat;

enter image description here

Dominika Es
  • 39
  • 1
  • 6

3 Answers3

3

useEffect runs during the first render and all subsequent updates.

I believe you might need to explicitly perform setLadyDancing to true or false depending on the state that you want them to be, otherwise it will keep getting toggled based on its previous state.

Maybe this might work:

useEffect(() => {
        if (isPlaying) {
            audioElement.current.play();
            setLadyDancing(true);
            setCatDancing(true);
        } else {
            audioElement.current.pause();
            setLadyDancing(false);
            setCatDancing(false);
        }
    }, [isPlaying]);
Aaron T
  • 442
  • 1
  • 6
  • I do think it's best to set false/true explicitly. But it won't get triggered by the updates in this function. Only of isPlaying – Ethan Van den Bleeken Jul 08 '22 at 11:25
  • Which is controlled by the `playPause` function in this case – per the author's requirement? I'm not sure what else it should do. – Aaron T Jul 08 '22 at 11:27
  • I misunderstood your answer. I thought you said this function will update based on the setting of `ladyDancing` and `catDancing`. Nevermind my comment, this is good. – Ethan Van den Bleeken Jul 08 '22 at 11:32
1

Just use isPlaying state for images.

import { useEffect, useRef, useState } from 'react'

import styled from './Cat.module.scss'
import Swing from '../../assets/audio/GirlsLikeToSwing.mp3'

let dancingImg = ['https://i.gifer.com/P6XV.gif', 'https://i.gifer.com/DCy.gif']

const Cat = () => {
  const audioElement = useRef()
  const [isPlaying, setIsPlaying] = useState(false)

  const toggleAudio = () => {
    setIsPlaying((prevState) => !prevState)
  }

  return (
    <div className={styled.headerContainer}>
      <div className={styled.lady}>
        {isPlaying ? <img src={dancingImg[0]} alt="" className={styled.ladyDancing} /> : null}
        {isPlaying ? <img src={dancingImg[1]} alt="" className={styled.catDancing} /> : null}
      </div>

      <button onClick={toggleAudio}>Play</button>

      <audio src={Swing} ref={audioElement}></audio>
    </div>
  )
}

export default Cat
bogdanoff
  • 1,705
  • 1
  • 10
  • 20
0

The state is true when the page loads because on the initial render the useEffect block gets called and it's setting them to !ladyDancing and !catDancing which makes it true.

I recommend using a separate function for it (and remove useEffect).

And also about the pictures: you are setting them to their values when isPlaying is true. To fix that, you would need to set them to true (when playing) or false (when not playing).

The code:

const playPause = () => {
    setIsPlaying(!isPlaying);
    toggle(); // call the function
}

const toggle = () => { // the name of the function can be whatever you want
    if (isPlaying) {
        audioElement.current.play();
        setLadyDancing(true);
        setCatDancing(true);
    } else {
        audioElement.current.pause();
        setLadyDancing(false);
        setCatDancing(false);
    }
}
User456
  • 1,216
  • 4
  • 15