How two use setInterval in react hook ?
Background
I get a task for HTTP polling to keep the latest message from the server. then I use setInterval for it.
import "./styles.css";
import { useEffect, useState} from 'react'
export default function App() {
const [content, setContent] = useState(0)
useEffect(() => {
const intervalId = setInterval(() => {
setContent(content + 1)
}, 1 * 1000)
return () => clearInterval(intervalId)
}, [])
return (
<div className="App">
{content}
</div>
);
}
the page always outputs 1, the content was not the latest value when you used the setInterval. so what happened in this situation?
Let's try another way, remove the [] of the effects, then the content was updated correctly. but effects create precise intervals every time.
Problems
the callback function of the setInterval function is a Closure, as we all know the chain scope of Javascript. the effect function gets the init value of content because it executes once, then the callback of setInterval can only access the init value.
Solutions
- useState((preState) => {})
you can use preState to compute the latest value.
useRef
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
useReducer
React guarantees that dispatch function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.
Conclusion
use the custom hook, it's simple.
function useInterval(callback) {
const savedCallback = useRef();
useEffect(() => {
savedCallback.current = callback;
});
useEffect(() => {
function tick() {
savedCallback.current();
}
let id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []);
}
Reference
- https://reactjs.org/docs/hooks-reference.html#usereducer
- https://overreacted.io/zh-hans/making-setinterval-declarative-with-react-hooks/