skip to Main Content

I am trying to update a react state, referenced as an array position index.
So, I want the state to be updated in a way that the position should be updated back and forth.

Like the below,

0 -> 1 -> 2 -> 1 -> 0 -> 1 -> 2 -> 1 -> 0 -> 1 -> 2 -> 1 -> 0 …….

Now, below is the code, I am using to update as expected above.


import React, { useEffect, useState } from "react";
import "./02-LandStyle.scss";

export default function IntroLine() {
    const positions = ["0vh", "-6vh", "-12vh"];
    const [posNo, setposNo] = useState(0);
    let goReverese = false;

    useEffect(() => {
        const updatePos = setInterval(() => {
            console.log(posNo, goReverese);
            if (goReverese == false) {
                if (posNo == 2) {
                    goReverese = true;
                } else {
                    setposNo((t) => t + 1);
                }
            }

            if (goReverese == true) {
                if (posNo < 0) {
                    goReverese = false;
                } else {
                    setposNo((t) => t - 1);
                }
            }
        }, 2000);

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

    return (
        <div className="intro-text">
            <h2>Delivers </h2>
            <div className="inner-container">
                <div
                    className="inner-box"
                    // style={{ top: `${positions[posNo]}` }}
                >
                    <div className="slogan-item">
                        <h2>FOOD</h2>
                    </div>
                    <div className="slogan-item">
                        <h2>LOVE</h2>
                    </div>
                    <div className="slogan-item">
                        <h2>HAPPINESS</h2>
                    </div>
                </div>
            </div>
        </div>
    );
}


But instead, it is behaving like the below.

0 -> 1 -> 2 -> 1 -> 2 -> 1 -> 2 -> 1 -> 2 …..

This was found to be a simple bug, but I couldn’t trace it somehow.
Please help.

4

Answers


  1. Replace (posNo < 0) with (posNo <= 0). Hope it works.

    Login or Signup to reply.
  2. You should add the 2nd paramter in useEffect, otherewise, useEffect will execute in each render loop as the state changed inside it.
    try this

      useEffect(() => {
            const updatePos = setInterval(() => {
                console.log(posNo, goReverese);
                if (goReverese == false) {
                    if (posNo == 2) {
                        goReverese = true;
                    } else {
                        setposNo((t) => t + 1);
                    }
                }
    
                if (goReverese == true) {
                    if (posNo < 0) {
                        goReverese = false;
                    } else {
                        setposNo((t) => t - 1);
                    }
                }
            }, 2000);
    
            return () => {
                clearInterval(updatePos);
            };
        },[]); <--- add the dependencies paramter. empty array means only execute once
    

    checkout the reference doc here

    Login or Signup to reply.
  3. import React, { useEffect, useRef, useState } from "react";
    
    export default function IntroLine() {
      const positions = ["0vh", "-6vh", "-12vh"];
      const [posNo, setposNo] = useState(0);
      let goReverese = useRef(false);
    
      useEffect(() => {
        const updatePos = setInterval(() => {
          console.log(posNo, goReverese);
          if (goReverese.current === false) {
            if (posNo === 2) {
              goReverese.current = true;
            } else {
              setposNo((t) => t + 1);
            }
          }
    
          if (goReverese.current === true) {
            if (posNo <= 0) {
              goReverese.current = false;
            } else {
              setposNo((t) => t - 1);
            }
          }
        }, 1000);
        return () => {
          clearInterval(updatePos);
        };
      });
    
      return (
        <div className="intro-text">
          <h2>Delivers </h2>
          <div className="inner-container">
            <div
              className="inner-box"
              // style={{ top: `${positions[posNo]}` }}
            >
              <div className="slogan-item">
                <h2>FOOD</h2>
              </div>
              <div className="slogan-item">
                <h2>LOVE</h2>
              </div>
              <div className="slogan-item">
                <h2>HAPPINESS</h2>
              </div>
            </div>
          </div>
        </div>
      );
    }
    
    
    Login or Signup to reply.
  4. After changing posNo, the component rerenders and another interval gets called. I think it’s better to use useRef. Also never compare a boolean like this : goReverese == true. just use goReverese instead.
    Also never use useEffect without a dependency.
    Try this. It will give you the result you want:

    import React, { useEffect, useState, useRef } from "react";
    import "./02-LandStyle.scss";
    
    export default function IntroLine() {
        const positions = ["0vh", "-6vh", "-12vh"];
        const posNo = useRef(0);
        let goReverese = false;
    
        useEffect(() => {
           const updatePos = setInterval(() => {
              console.log(posNo.current, goReverese);
              if (goReverese) {
                   if (posNo.current === 1) {
                      goReverese = false;
                   }
                   posNo.current -= 1;
              } else {
                 if (posNo.current === 1) {
                    goReverese = true;
                 }
                 posNo.current += 1;
              }
         }, 2000);
    
        return () => {
            clearInterval(updatePos);
         };
       }, []);
    
        return (
            <div className="intro-text">
                <h2>Delivers </h2>
                <div className="inner-container">
                    <div
                        className="inner-box"
                        // style={{ top: `${positions[posNo]}` }}
                    >
                        <div className="slogan-item">
                        <h2>FOOD</h2>
                    </div>
                    <div className="slogan-item">
                        <h2>LOVE</h2>
                    </div>
                    <div className="slogan-item">
                        <h2>HAPPINESS</h2>
                    </div>
                </div>
            </div>
        </div>
       );
       }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search