0

I'm trying to create an Image changing component in Gatsby/React from the results on a Graphql query. I am stuck and don't know what to do inside that setInterval function. Any Help appreciated. Thanks

import React, { useState, useEffect } from "react";
import { graphql, useStaticQuery } from "gatsby";
import Img from "gatsby-image";

function ImgRotator() {
   const imageData = useStaticQuery(graphql`
      query {
         allFile(filter: { extension: { regex: "/(jpg)|(jpeg)|(png)/" }, relativeDirectory: { eq: "rotator" } }) {
            edges {
               node {
                  id
                  childImageSharp {
                     fluid(maxWidth: 2880) {
                        ...GatsbyImageSharpFluid_withWebp
                     }
                  }
               }
            }
         }
      }
   `);

   const allImages = imageData.allFile.edges;
   const [images] = useState(allImages);
   const [displayImage, setDisplayImage] = useState(imageData.allFile.edges[0].node.childImageSharp.fluid);


   useEffect(() => {
      const interval = setInterval(() => {

         // Help! Don't know what to do here

      }, 1000);
      return () => clearInterval(interval);
   }, [displayImage]);

   return <Img fluid={displayImage} alt="" loading="eager" />;
}

export default ImgRotator; 
 
Ferran Buireu
  • 28,630
  • 6
  • 39
  • 67
Paolo_T
  • 29
  • 4
  • it's not hard to imagine that you need a counter/index (state) and should be increased in interval, reseted on max ... basic react state action ... autoincreasing counter examples – xadm Dec 18 '20 at 12:59

1 Answers1

1

One workaround that may work for you is:

    import React, { useState, useEffect } from "react";
    import { graphql, useStaticQuery } from "gatsby";
    import Img from "gatsby-image";
    
    function ImgRotator() {
       const imageData = useStaticQuery(graphql`
          query {
             allFile(filter: { extension: { regex: "/(jpg)|(jpeg)|(png)/" }, relativeDirectory: { eq: "rotator" } }) {
                edges {
                   node {
                      id
                      childImageSharp {
                         fluid(maxWidth: 2880) {
                            ...GatsbyImageSharpFluid_withWebp
                         }
                      }
                   }
                }
             }
          }
       `);
    
       const allImages = imageData.allFile.edges;
       const [currentImage, setCurrentImage]=useState({});
       const [currentIndex, setCurrentIndex]=useState(0);
    
    
       useEffect(() => {
          const interval = setInterval(() => {
                 
            setCurrentImage(allImages[currentIndex].node.childImageSharp.fluid)
             setCurrentIndex(currentIndex++)
                  
          }, 1000);
          return () => clearInterval(interval);
       }, [currentIndex]);
    
       return <Img fluid={currentImage} alt="" loading="eager" />;
    }
    
    export default ImgRotator; 

Summarizing, you initially set two states:

   const [currentImage, setCurrentImage]=useState({});
   const [currentIndex, setCurrentIndex]=useState(0);

Quite self-explanatory: currentImage will store your displayed image and currentIndex the index in the array of that image.

In your useEffect (triggered once the currentIndex changes) you've defined an interval where it looks for the currentIndex in allImages and sets the currentImage using that index. Each second the function is being triggered again so it will update the system.

Your gatsby-image will show the currentImage based on that interval:

   return <Img fluid={currentImage} alt="" loading="eager" />;
Ferran Buireu
  • 28,630
  • 6
  • 39
  • 67
  • Thank you very much Ferran. Your solution works. Only one problem in my setup: The images are flashing between changes like they get unmounted and remounted Is there a way to get a a smooth change with no gaps between images? – Paolo_T Dec 18 '20 at 13:08
  • 1
    @FerranBuireu not in 'pure react' ... old node is just removed, no fadeOut/slideOut possible ... css can only affect appearing elements ... you have to use some react animation lib ... or try to render 2 (prev+current) images with keys – xadm Dec 18 '20 at 13:53