-1

I'm trying to make a loop with a few images using React.

I made this logical, and for me, it should work, but it doesn't! I did the same logical for one html page with javascript, but in a React Component it isn't working.

export default function AnimatedImage() {
    const [imageRoute, setImageRoute] = useState('source1')
    const [frame, setFrame] = useState(1)
    
    const imagesPath = [
        "source1",
        "source2",
        "source3",
        "source4"
    ]
    
    function handleAnimation(){
        setImageRoute(imagesPath[frame])

        if (frame === 3)
            setFrame(0)

        setFrame(frame + 1)
    }

    setInterval(handleAnimation, 300);
   
    return (
        <img src={imageRoute} />
    )
}

I'm having some issues with the image itself, sometimes it disappear showing the icon of "no image found", and in a few milliseconds, it appear again. And sometimes the interval get crazy and start to loop really fast ignoring the time set.

This only happens here in React.

Nothing make sense here Someone can help?

2 Answers2

4

Since your setInterval call is in your render function, a new timer is created every time you modify the state of your component. Eventually, you have a lot of timers running simultaneously and stepping on each other to change the image source. You should use the useEffect hook to make sure the timer is only set once when the component mounts and clears the interval when it unmounts.

In addition, I would also use a single piece of state to track which image is being shown to avoid your frame and source from getting out of sync.

const imagesPath = [
    "source1",
    "source2",
    "source3",
    "source4"
]

export default function AnimatedImage() {
    const [frame, setFrame] = useState(0)

    useEffect(() => {
        const handleAnimation = () => {
            setFrame(frame => (frame + 1) % imagesPath.length)
        }
        const interval = setInterval(handleAnimation, 300) // 300 ms is pretty short. Did you mean 3000?
        return () => clearInterval(interval)
    }, [])
   
    return (
        <img src={imagesPath[frame]} />
    )
}

const { useState, useEffect } = React;

const imagesPath = [
    "https://via.placeholder.com/150/E34234/FFF",
    "https://via.placeholder.com/150/458E00/FFF",
    "https://via.placeholder.com/150/8B65E7/FFF",
    "https://via.placeholder.com/150/C06500/FFF"
];

function AnimatedImage() {
    const [frame, setFrame] = useState(0)

    useEffect(() => {
        const handleAnimation = () => {
            setFrame(frame => (frame + 1) % imagesPath.length)
        }
        const interval = setInterval(handleAnimation, 300) // 300 ms is pretty short. Did you mean 3000?
        return () => clearInterval(interval)
    }, [])
   
    return (
        <img src={imagesPath[frame]} />
    )
}

ReactDOM.render(<AnimatedImage />, document.querySelector("#animated-image-container"));
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<div id="animated-image-container"></div>
3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
Elan Hamburger
  • 2,137
  • 10
  • 14
-2

It is related with hooks rules. https://reactjs.org/docs/hooks-rules.html Try to use arrow function with the handle animation

const handleAnimation = () => { /** your code */}
Dani
  • 745
  • 7
  • 12