Search code examples
cssreactjscss-animationsreact-functional-component

React prevent css animation restarting every state change


I have two child components, an image gallery and a hidden components which will display the clicked image of the gallery at full size.

    const [selectedIndex, setSelectedtIndex] = useState(0);
    const [galleryVisible, setGalleryVisible] = useState(false);

    const FirstComponent = () => {
        return (
           <div className={'gallery-container'}>
               <div class='img fade-1' onClick={() => handleClick(1)}>Image 1</div>
               <div class='img fade-2' onClick={() => handleClick(2)}>Image 2</div>
               <div class='img fade-3' onClick={() => handleClick(3)}>Image 3</div>
               [...]
           </div>
        )
    }

    const handleClick = (index) => {
        setSelectedtIndex(index)
        setGalleryVisible(true)
    }

    const SecondComponent = ({ index }) => {
        return (
            <div className={`selected-img`}>Selected : {index} (But the fading animation shouldn't restart è_é)</div>
        )
    }

    return (
        <>
            <FirstComponent/>
            {galleryVisible && 
                <SecondComponent index={selectedIndex} />
            }
        </>
    )

The issue is that I also have a fade-in animation on the first component, and every time I click on an image to display the second component, that animation resets due to the rerender of react on state change.

    &.fade-1 {
      animation-delay: 0s
    }
    &.fade-2 {
      animation-delay: 0.5s
    }
     &.fade-3 {
      animation-delay: 1s
    }

I don't know how can I only change the second component when the state is changed from a click on the first one... I tried to play with useMemo but couldn't get it to work.

Here is a codepen reproducing the issue : https://codepen.io/disgallion/pen/zYjqPBE


Solution

  • Move the first component outside of App and pass handleClick to its props:

    
      const FirstComponent = ({handleClick}) => {
        return (
          <div className={'gallery-container'}>
            <div class='img fade-1' onClick={() => handleClick(1)}>1</div>
            <div class='img fade-2' onClick={() => handleClick(2)}>2</div>
            <div class='img fade-3' onClick={() => handleClick(3)}>3</div>
          </div>
        )
      }
    
    const App = () => {
      const [selectedIndex, setSelectedtIndex] = useState(0);
      const [galleryVisible, setGalleryVisible] = useState(false);
    
      const handleClick = (index) => {
        console.log('click')
        setSelectedtIndex(index)
        setGalleryVisible(true)
      }
    
    
      
      const SecondComponent = ({ index }) => {
        return (
          <div className={`selected-img`}>Selected : {index} (But the fading animation shouldn't restart è_é)</div>
        )
      }
      
      return (
        <>
          Click on an image to open the fullsize gallery:
          <FirstComponent handleClick={handleClick} />
          {galleryVisible && 
            <SecondComponent index={selectedIndex} />
          }
        </>
      )
    }
    
    ReactDOM.render(<App />,
    document.getElementById("root"))
    

    Now the state change of App will not trigger a re-render in FirstComponent