Home > front end >  Why first one is working and others not setting index to zero?
Why first one is working and others not setting index to zero?

Time:01-24

I am making a NewsCardComponent which will display slideshow of images provided in an array which will be passed down as a prop. Everytime the component is used, will have a different number of elements in images array. so i put the "imgArr" in src of img as: <img src={imgArr[index]}> where "index" is the state and i have to dynamically check if a component has reached to the end of array then setIndex to zero. I have achieved what I wanted but i dont know why all the techniques other than first are not working.

My useEffect:

useEffect(() => {
const interval = setInterval(() => {
indexResetter();
}, 2000);

    return () => {
      clearInterval(interval);
    };

}, []);

Technique 1 (working fine) :

function indexResetter() {
   setIndex((prev) => {       
       let newIndex = prev   1;       
       if (newIndex > imgArr.length - 1) {                 
           newIndex = 0;       
       }       
      return newIndex;     }); }

Technique 2 (state is not setting to zero but increasing infinitely):

function indexResetter() {
    let newIndex = index   1;
    if (newIndex === imgArr.length - 1) {
       setIndex(0);
     } else {
       setIndex((prev) => prev   1);
     }
 }

Technique 3 (same problem with second one):

function indexResetter() {
    if (index >= imgArr.length - 1) {
       setIndex(0);
     } else {
       setIndex((prev) => prev   1);
     }
 }

CodePudding user response:

From what I am reading, your use effect only runs during the first render. Put index in the dependency array in your useEffect so that it runs everytime that your index changes

CodePudding user response:

In short, your useEffect() runs on initial mount, meaning that the setInterval() continues to execute the indexResetter function that was defined on the initial render, even after subsequent rerenders. That means the version of the indexResetter function that you end up executing only knows about the index state upon the initial mount, giving you the issue where your index doesn't change.


For more details, when you define a function, it stores a reference to the "scope" it's defined in, which includes all the variables defined in that scope:

function NewsCardComponent() {
  const [index, setIndex] = useState(0);

  function indexResetter() {
    ...
  }
}

When your component renders, it does the following:

  1. NewsCardComponent() gets called, creating a new "scope" (formally an environment record). This scope holds the variables such as index
  2. The indexResetter function gets created (it's not being called yet). This function stores an internal reference to the scope created at step 1. This is called a closure.

Later on, when indexResetter() gets called, it uses the scope that it stored internally at step 2 to work out where to find look for index if it can't find it within the indexResseter function itself.

When you update your index state using setIndex(), your component rerenders, and performs the above two steps 1 and 2 again. Creating a new scope for the NewsCardComponent that now holds the updated value of index, as well as creating a new indexResetter function. This means that each time you call setIndex, you effectively create new versions of indexResster function that can "see" the new vale of index.

Your problem is that your useEffect() only runs on the initial mount of your function, so the function that you're calling within your setInterval() is the initial indexResetter function that only has visibility of index state for when your component initially mounted:

const interval = setInterval(() => {
  indexResetter();
}, 2000);

On subsequent rerenders, the indexResetter function will be redeclared, but the above setInterval() will continue to call the version of the indexResster function that was defined on the initial render, which only knows about the index state at that time. As a result, the value of index within the function ends up always being the initial value.

In your working example, you're using the state setter function to access prev, which means you're no longer relying on the index value from the surrounding scope. The state setter function will provide you with the most up to date value of your state, and so you don't face the same issue.

  • Related