Jeffrey Bentley
Jeffrey Bentley

Reputation: 43

I am trying to restart a GIF animation in a React using hooks

I have found similar questions with answers I have tried to modify I looked at this one in particular. The goal here is to get the GIF to restart every time this component is shown. My sample code has been through an iteration or two, and this is where it is as of now.

import {useState, useEffect} from "react"
import Footer from "../components/Footer";
import NavMenu from '../components/NavMenu'
import {Box, Typography} from '@mui/material'
import backgroundImage from '../assets/images/world-bg.png'
import Logo from '../assets/images/[email protected]'
import landscape from '../assets/gifs/why-we-exist_1test.gif'
import { Gif } from "@mui/icons-material";


const Exist = () => {

    const [gif, setGif] =useState('')

    const reloadGif = () => {
        setGif('')
        setTimeout(() => {
          setGif(landscape)
        }, 0)
      }

    useEffect(() => {
       reloadGif()
        return () => {
            setGif('')
        }
    },[]
    )

    const styles ={
        introContainer:{
            height: '100%',
            width: '100vw',
            backgroundImage: `url(${backgroundImage})`,
            display: 'flex',
            flexDirection: 'column',
            alignContent: 'center',
            color:'white',
            textAlign:'center',
            justifyContent: 'center',
            alignContent:'center',
            flexShrink:0
        }
    }

    return (
        <Box sx = {styles.introContainer}>
            <NavMenu />
            <Box >
              {gif !== '' && <img src={gif} alt="Logo"></img>}
            </Box>
            <Footer logo={false} back={'intro'} next={"home"}/>
        </Box>
    )
}

export default Exist;

I was hoping that my useEffect would clear the image src and then set it again forcing a re-render. The problem I am seeing is that with the empty dependency array it won't trigger a re-render, but without it, I get constant re-renders, but with the GIF still only playing once.

Upvotes: 1

Views: 3786

Answers (2)

Carolin
Carolin

Reputation: 121

The issue here is most likely that you don't have a clear way to retrigger the use effect to make the Gif reload.

There are several solutions to this, but it depends on how you implemented the logic to show and not show your component.

As you already realized a useEffect with an empty dependency array is triggered whenever the component is put on the screen.

As a solution you need to check out how you are currently rendering your component with the gif to the screen. You have to make sure it is newly added to the DOM, whenever you want to play the gif.

As a side note: There is no need for the return function in your useEffect.

const ParentComponent = () => {
  const [showChildComponent, setShowChildComponent] = useState(true)


  return (
 <>
   <button onClick={() => {setShowChildComponent(!showChildComponent)}}>
    Click me to show or hide the child component
   </button>
   { showChildComponent && <ChildComponent /> }
 </>
  )
}

const ChildComponent = () => {
   const [gif, setGif] =useState('')

const reloadGif = () => {
    setGif('')
    setTimeout(() => {
      setGif(landscape)
    }, 0)
  }

useEffect(() => {
   reloadGif()
  },[]
)

 return (<img src={gif} alt="Logo"></img>) 
}

You can also retrigger a useEffect with the help of a prop / state. The following useEffect would be retriggered everytime your showGif state now changes, which you could set whenever you want to play your gif.

   const [retriggerGif, setRetriggerGif] = useState(true)

    useEffect(() => {
       reloadGif()
    },[retriggerGif]
    )

In case you can not mount / unmount your component

In the comments you mentioned you use a hash based router and only wrap your component in a RouterLink.

In this case you could make your useEffect depend on the hash of your router and always rerun of the hash changes:

This of course depends on what router you are using but there is always a way to get the currently active path and hash somehow.

In react-router you e.g. can get the history object through the useHistory() hook like this:

const history = useHistory() 

useEffect(() => { 
    // check that you are at the correct hash 
    if(history.hash === 'header') { 
        reloadGif() 
    } 
}, [history]) 

Upvotes: 1

Jeffrey Bentley
Jeffrey Bentley

Reputation: 43

Using @Carolin's answer as a starting point. I was able to come up with a solution, still feels hacky but it works.

import {useEffect} from "react"
import Footer from "../components/Footer";
import NavMenu from '../components/NavMenu'
import {Box} from '@mui/material'
import backgroundImage from '../assets/images/world-bg.png'
import landscape from '../assets/gifs/why-we-exist_1test.gif'




const Exist = ({viewCount, handleView}) => {


useEffect(() => {
    return () => {
     return handleView(viewCount + 1)
    }
},[])

const styles ={
    introContainer:{
        height: '100%',
        width: '100vw',
        backgroundImage: `url(${backgroundImage})`,
        display: 'flex',
        flexDirection: 'column',
        alignContent: 'center',
        color:'white',
        textAlign:'center',
        justifyContent: 'center',
        alignContent:'center',
        flexShrink:0
    }
}

return (
    <Box sx = {styles.introContainer}>
        <NavMenu />
        <Box >
        {viewCount %2 === 0 && <img src={landscape + "?=" + Date.now()} alt="Logo"></img>}
        {viewCount %2 !== 0 && <img src={landscape + "?=" + Date.now()} alt="Logo"></img>}
        </Box>
        <Footer logo={false} back={'intro'} next={"home"}/>
    </Box>
  )
}

export default Exist;

where viewCount and handleView are const [count, setCount]=useState(0) props from parent component

Upvotes: 1

Related Questions