Search code examples
javascriptreactjsuse-statereact-state-management

Usestate increment by 1 not working in setInterval


    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.


Solution

  • 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.