Search code examples
reactjsreact-hooksnext.jssettimeoutuse-state

React(Next.js) Set Timeout and useState not doing as should


So I basically want a tag that looks like this.

This is <a href="https://nextjs.org">tyty<span> {textArray[Index]}</span></a>

and I want textArray[Index] to show a different value every 3 seconds. Currently my code cycles through the array, however, it seems to gofrom Array 0 to Array 2 then Array 4.

I want it to Show array 0 then 3 seconds later array 1, 3 secs, array 2, 3 secs, array 3, then back to array 0 and repeat.

My code looks like this:

  const textArray = [
    "digital",
    "development",
    "graphics",
    "blog"
  ] 

const [Index, setIndex] = useState(0);

  const intervalID = setTimeout(changeText, 3000);

  function changeText(){
    if(Index >= 0 && Index < 4) {
    setIndex((prevIndex) => ++prevIndex);
    } 

    if(Index > 3){
        setIndex((prevIndex) => prevIndex=0);
    }
  }

Why can't I also seem to get it to go through array 1 and array 3.

When I go to google dev mode, I type in Index into the console and it says its not defined. Same with textArray.


Solution

  • First - memoize your array or other objects using useState or useMemo. Without that - objects and arrays will be recreated on each render. And if for primitives like boolean, numbers, strings it is +- ok and depsArrays of useMemo and useEffect will handle them right - for arrays and object it will not be ok due to even if object is the same in terms of values - reference to this object will be changed (new object created) and that will cause pretty bad behavior and tons of unneded re-renders, especially if you pass those objects and arrays down to the child components.

    Next - it is setInterval, not setTimeout.

    Last - always clear Intervals you created.

    import { useState, useEffect, useMemo } from "react";
    
    export default function App() {
      // memoize the array and dont recreate it on each render
      const textArray = useMemo(
        () => ["digital", "development", "graphics", "blog"],
        []
      );
      const [index, setIndex] = useState(0);
    
      // Will be recalculated when index or textArray changed
      const text = useMemo(() => {
        return textArray[index];
      }, [index, textArray]);
    
      useEffect(() => {
        // setInterval, not the setTimeout
        const intervalId = setInterval(() => {
          // Index will go up but % will cut it down
          setIndex((prev) => (prev + 1) % textArray.length);
        }, 3000);
    
        return () => clearInterval(intervalId);
      }, [textArray]);
    
      return (
        <div className="App">
          This is{" "}
          <a href="https://nextjs.org">
            tyty<span> {text}</span>
          </a>
        </div>
      );
    }
    
    

    Edit laughing-currying-nlhhze