one-hand-octopus
one-hand-octopus

Reputation: 2743

State not update when using setter and setInterval in useEffect

I have the following code, which I expect it to print an extra 10 every 2 seconds on the screen. However, it only prints 10 once. console.log(digits) gave me the correct array of 10s. How can I add a 10 to the array every 2 seconds and print the updated array on the screen each time the 10 is added?

Code sandbox here: https://codesandbox.io/s/silly-https-zk58p?file=/src/App.js

import { useEffect, useState } from "react";
let data = [];
export default function App() {
  const [digits, setDigits] = useState([]);
  useEffect(() => {
    let timer = setInterval(() => {
      data.push(10);
      setDigits(data);
      console.log(digits);
    }, 2000);
    return () => clearInterval(timer);
  }, [digits]);

  return <div className="App">{digits}</div>;
}

Upvotes: 0

Views: 110

Answers (2)

Lith
Lith

Reputation: 1325

The issue is with setDigits(data). With arrays, you should be executing setDigits([...data]).

Another way would be doing this:

let timer = setInterval(() => { 
    setDigits([...digits, 10]); 
}, 2000);

Whenever dealing with objects, you should treat them as immutables. What happened here is you modifying an array and puhsing the SAME array into a state. While the value might be different, the array is actually the same hence it does not update the render. Whenever doing [...data] it creates a new array with the same values hence trigger the update.

useEffect picks up the new value change hence why it fires again(cant be observed by console.log()), but this does not trigger re-render of the component.

Upvotes: 1

Medi
Medi

Reputation: 1036

In your code, you are mutating the same array data by pushing 10 after each 2 seconds. As a result, the useEffect is not executing in the subsequent renders. So either you spread data array as in the following code snippet or simply get rid of the data variable and rely on digits array as Lith suggested.

export default function App() {
  const [digits, setDigits] = useState([]);
  useEffect(() => {
    let timer = setInterval(() => {
      data = [...data, 10];
      setDigits(data);
      console.log(digits);
    }, 2000);
    return () => clearInterval(timer);
  }, [digits]);

  return <div className="App">{digits}</div>;
}

Upvotes: 1

Related Questions