Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't read latest state variable in setInterval (Hooks) [duplicate]

I suppose this is because how JS works, but you wouldn't have this problem with classes I suppose. In this code:

  let [open, setOpen] = React.useState(false);
  let [counter, setCounter] = React.useState(0);

  function handleClick() {
    setOpen(true);

    setInterval(() => {
      console.log(counter);
      setCounter(counter + 1);
    }, 2000);
  }

If I call handleClick once (e.g. click button), the value logged on console is always 0 (despite state being updated each time).

This is probably because of the closure. But what if I wanted to see the most recent value of counter in such setup? With classes you could have done this.state.counter and it would read latest value. Is there some workaround with hooks? demo.


Note: I found this question which asks basically the same. Somehow I didn't encounter it on initial search.

like image 798
Giorgi Moniava Avatar asked Sep 12 '19 10:09

Giorgi Moniava


People also ask

Why setState is not updating immediately?

setState , and React. useState create queues for React core to update the state object of a React component. So the process to update React state is asynchronous for performance reasons. That's why changes don't feel immediate.

Does setInterval repeat?

The setInterval() method repeats a given function at every given time-interval. window. setInterval(function, milliseconds); The window.

How do you use setInterval in react hooks?

setInterval is a method that calls a function or runs some code after specific intervals of time, as specified through the second parameter. For example, the code below schedules an interval to print the phrase: “Interval triggered” every second to the console until it is cleared.


1 Answers

Check the next example, refer to setState in react docs.

export default function SimpleSnackbar() {
  const classes = useStyles();
  let [open, setOpen] = React.useState(false);
  let [, setCounter] = React.useState(0);

  // Save reference for clearing the interval
  // in your case you firing an interval on every click
  const lastInterval = useRef();

  // Side effect on `open` change.
  useEffect(() => {
    if (lastInterval.current) clearInterval(lastInterval.current);
    if (open) {
      lastInterval.current = setInterval(() => {

        // Functional setState
        setCounter(prevCounterValue => {
          console.log(prevCounterValue);
          return prevCounterValue + 1;
        });

      }, 1000);
    }
  }, [open]);

  function handleClick() {
    setOpen(true);
  }

  function handleClose(event, reason) {
    if (reason === 'clickaway') {
      return;
    }
    setOpen(false);
  }

  ...
}

Edit Material demo

like image 130
Dennis Vash Avatar answered Nov 09 '22 19:11

Dennis Vash