Home > Software design >  Get current state value from a function declared in a hook scheduled to run later
Get current state value from a function declared in a hook scheduled to run later

Time:10-26

Take the below code as an example.

import { useEffect, useState } from "react";


export default function Stopwatch(){
    const [stopwatch, setStopwatch] = useState(0);

    function updateStopwatch(){
        console.log("Updating stopwatch from ", stopwatch, " to ", stopwatch 1);
        setStopwatch(stopwatch   1);
    }

    useEffect(() => {
        const interval = setInterval(updateStopwatch, 1000);
        return () => clearInterval(interval);
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    return (
        <div>
            <h1>{stopwatch}</h1>
        </div>
    );
}

In this code, the function updateStopwatch will be invoked periodically without depending on any re-rendering of the component ( Although its invocation will cause a re-render ) and each time it will be invoked, it needs to retrieve the current up-to-date value of the state variable stopwatch, so it can update it accordingly. So that the stop watch will keep counting up.

However, what happens is that it only gets the value of stopwatch at the moment the function is declared. It's not really subscribed to a state variable but more like it just reads a constant once. And as a result the count show 1 and is stuck there.

So how can I make such a function get an up-to-date value of a state variable whenever it's invoked?

CodePudding user response:

In your setState method, you can retrieve a prevState argument, which is always the right value, here is the way you could do your function based on your example :

function updateStopwatch(){
    setStopwatch((prevState) => prevState   1);
}

CodePudding user response:

This is an issue of a stale closure over the initial stopwatch state value. Use a functional state update to correctly update from the previous state value instead of whatever value is closed over in callback scope at the time of execution. Move updateStopwatch declaration into the useEffect hook to remove it as an external dependency.

Example:

export default function Stopwatch(){
  const [stopwatch, setStopwatch] = useState(0);

  useEffect(() => {
    function updateStopwatch(){
      setStopwatch(stopwatch => stopwatch   1);
    }
    
    const interval = setInterval(updateStopwatch, 1000);
    return () => clearInterval(interval);
  }, []);

  return (
    <div>
      <h1>{stopwatch}</h1>
    </div>
  );
}
  • Related