1

I'm using next js for my web application. I want to write function that will update the scroll position using useState. For some reason, the useState isn't updating itself properly as I also need to calculate the maximum value from the function. Here is my code:

const [lazyProgress,setLazyProgress] = useState(0);
const [lazyProgressMax,setLazyProgressMax] = useState(0);


function lazyProgressUpdate(){
        const scrollTotal = document.documentElement.scrollTop;
        const heightWin = document.documentElement.scrollHeight - document.documentElement.clientHeight;
        const scroll = scrollTotal / heightWin * 100;

        setLazyProgress(scroll);
        if(lazyProgress > lazyProgressMax){
        setLazyProgressMax(lazyProgress);
    }
    console.log(lazyProgress,lazyProgressMax)

}

useEffect(()=>{

  const timer = setInterval(()=>{
          lazyProgressUpdate();
      },8000);
      return ()=>{
      clearInterval(timer);
      }
  },[]);

In the above snippet, lazyProgress and lazyProgressMax are always 0 in the function. So neither of them get updated.

How do i overcome this issue

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
Aadhit Shanmugam
  • 433
  • 1
  • 10
  • 23

2 Answers2

1

Issue

The issue you have is actually a stale enclosure of state in the setInterval callback. This callback closes over the initial state so that's all it'll ever see.

Solution

Enqueue the scroll updates and use a separate useEffect hook with a dependency on lazyProgress to check/set the lazyProgressMax state.

const [lazyProgress, setLazyProgress] = useState(0);
const [lazyProgressMax, setLazyProgressMax] = useState(0);

function lazyProgressUpdate(){
  const scrollTotal = document.documentElement.scrollTop;
  const heightWin = document.documentElement.scrollHeight - document.documentElement.clientHeight;
  const scroll = scrollTotal / heightWin * 100;

  setLazyProgress(scroll); // <-- (1) update lazyProgress state
}

useEffect(() => {
  const timer = setInterval(() => {
    lazyProgressUpdate();
  }, 8000);
  return () => {
    clearInterval(timer);
  }
},[]);

useEffect(() => {
  setLazyProgressMax(lazyProgressMax => { // <-- (3) functional state update
    if (lazyProgress > lazyProgressMax) {
      return lazyProgress; // <-- (4a) return new lazyProgress value
    }
    return lazyProgressMax; // <-- (4b) or previous state
  });
}, [lazyProgress]); // <-- (2) lazyProgress as dependency
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
1

You need to create function inside useEffect in case if function is not needed more.

Below code is guide to how create function inside useEffect and how to clear created interval.


    useEffect(()=>{

      const lazyProgressUpdate = () => {
         // function logic here
      }

      const timer = setInterval(() => {
             lazyProgressUpdate()
          }, 8000);
      
      return () => {
          clearInterval(timer);
      }
    },[]);