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)
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;