EDIT (22 June 2020): as this question has some renewed interest, I realise there may be a few points of confusion. So I would like to highlight: the example in the question
Instead of trying to access the most recent state within a callback, use useEffect
. Setting your state with the function returned from setState
will not immediately update your value. The state updates are batched and updated
It may help if you think of useEffect()
like setState
's second parameter (from class based components).
If you want to do an operation with the most recent state, use useEffect()
which will be hit when the state changes:
const {
useState,
useEffect
} = React;
function App() {
const [count, setCount] = useState(0);
const decrement = () => setCount(count-1);
const increment = () => setCount(count+1);
useEffect(() => {
console.log("useEffect", count);
}, [count]);
console.log("render", count);
return (
{count}
);
}
const rootElement = document.getElementById("root");
ReactDOM.render( < App / > , rootElement);
Update
You can create a hook for your setInterval
and call it like this:
const {
useState,
useEffect,
useRef
} = React;
function useInterval(callback, delay) {
const savedCallback = useRef();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
function Card(title) {
const [count, setCount] = useState(0);
const callbackFunction = () => {
console.log(count);
};
useInterval(callbackFunction, 3000);
useEffect(()=>{
console.log('Count has been updated!');
}, [count]);
return (
Active count {count}
);
}
const el = document.querySelector("#root");
ReactDOM.render( , el);
Some further info on useEffect()