Usestate increment by 1 not working in setInterval

Tags: , , ,



    const [currentPage, setCurrentPage] = useState(1);     
    const handleScroll = () => {
            const gridContainer = document.querySelector(".grid");
    
            const totalPages = Math.ceil(
                gridContainer.scrollWidth / gridContainer.clientWidth + -0.1
            );
            setTotalPages(totalPages);
    
            const scrollPos =
                gridContainer.clientWidth + gridContainer.scrollLeft + 2;
            if (gridContainer.scrollWidth > scrollPos) {
                gridContainer.scrollBy({
                    left: gridContainer.clientWidth + 20.5,
                    behavior: "auto",
                    block: "center",
                    inline: "center",
                });
                setCurrentPage(currentPage + 1);
            } else {
                setCurrentPage(1);
                gridContainer.scrollTo(0, 0);
            }
        };
    
        useEffect(() => {
            setInterval(() => {
                document.querySelector(".grid") && handleScroll();
            }, 5000);
        }, []);

For some reason this will never go past two when I run the setCurrentPage but if I increment it like this

 <button onClick={() => setCurrentPage(currentPage + 1)}>
                    +1
  </button

It works as expected. I am guessing it has something to do with the useEffect or the setInterval but not really a 100% sure why.

Answer

A new handleScroll function is created on every render in your code.

The function is passed to setInterval only the first time, so the currentPage inside this function will always stay 1, then 1 + 1 is always 2.

(A) Put handleScroll into the dependency array

A solution would be to create a new setInterval whenever there is a new handlescroll:

    useEffect(() => {
        let interval = setInterval(() => {   // <--- store the interval (to be able to remove it later)
            document.querySelector(".grid") && handleScroll();
        }, 500);

        // -- remove the interval inside the clean up function of useEffect
        return () => {
            clearInterval( interval );
        }
    }, [ handleScroll ]); // <--- add handleScroll to the dependency array

Obviously, in this case a setTimeout might be a better choice, as it always runs only once anyway.

(B) Pass a function to setState

Alternatively you can pass a function to setState:

setCurrentPage((prevCurrentPage) => prevCurrentPage + 1);

That is generally totally ok, but be careful not to lie about your effect’s dependencies.



Source: stackoverflow