Home > Blockchain >  Update state in setInterval not working as expected
Update state in setInterval not working as expected

Time:08-30

I'm new to react, today I encounter a very strange case, what I want todo is to get the data from server side every 5 seconds, but it doesn't work as expected, the problem can be simplied to this:

npx create-react-app my-app
cd my-app

edit the App.js

import { useState } from "react";


function App() {
  const [count, setCount] = useState(0)

  const increment = () => {
    // simulate the querying, for example, checking the status of server
    console.log('Current count value:', count)
    setCount(count   1)
  }

  setInterval(increment, 1000)


  return (
    <span>Count: {count}</span>
  );
}

export default App;

I want to update the state count every 1s, and I think the output in console would be

Current count value: 0
Current count value: 1
Current count value: 2
...

However, the output is very strange, click the link to see the output (I cant' insert image in the content)

chrome console output

Thanks

CodePudding user response:

You need to start interval in useEffect and clear when component unmount

import React, { useState, useEffect } from "react";

function App() {
  const [count, setCount] = useState(0)
  
  useEffect(() => {
    const increment = () => {
      // simulate the querying, for example, checking the status of server
      console.log('Current count value:', count)
      setCount(prev => prev   1) // You can't use count state because it's will trigger useEffect and cause infinite loop. You need to use callback to update from previous value
    }

    const intervalId = setInterval(increment, 1000) // setInterval return interval id

    return () => {
      // Clear interval using intervalId
      // This function run when component unmount
      clearInterval(intervalId)
    }
  }, [])
  
  return (
    <span>Count: {count}</span>
  );
}

export default App;

CodePudding user response:

You can use setTimeout instead setinterval-is-moderately-evil

const DURATION = 1000;
const [count, setCount] = React.useState(0);

React.useEffect(() => {
  setTimeout(() => {
    setCount(count   1);
  }, DURATION);
});

You can read more of how to use setInterval in react here making-setinterval-declarative-with-react-hooks

CodePudding user response:

You need to use useEffect hook to keep track of changes in the component's states.

trying to invoke a function inside the body of your component will make that function to re-invoke everytime any DOM is updated. The best practice to initiate your component is to put it inside a useEffect hook without dependencies.

useEffect(() => {
    setInterval(increment, 1000)
}, []);

Now if you want to keep track of a state when it's updated you can use anotther useEffect and add the state in dependency array. Like:

useEffect(() => {
    console.log('incremented', count)
}, [count]);

Here is an edited version of your code:

import { useState, useEffect } from "react";

function App() {
    const [count, setCount] = useState(0)

    // a function that changes count state
    const increment = () => {
        setCount(prevState => prevState   1)
    }

    // when component is mounted
    useEffect(() => {
        setInterval(increment, 1000)
    }, []);

    // when count state changes
    useEffect(() => {
        console.log('incremented', count)
    }, [count]);

    return (
        <span>Count: {count}</span>
    );
}

export default App;
  • Related