Javascript binds variables to functions as they are created, which can cause ‘dangling’ out-of-date values to be used in useEffect
.
function Counter() {
var [count, setCount] = useState(1);
useEffect(() => {
var timeout = setInterval(() => setCount(count => count+1), 1000); // every 1'
setTimeout(() => console.log(`Count after 10': ${count}`), 10000); // after 10'
return () => clearTimeout(timeout);
}, []);
return <span>{count}</span>
}
The anonymous function of setInterval
binds to count
when it’s 1
, and therefore the console.log
output isn’t 10
(but 1
).
Is there an elegant fix? For example, adding count
as a dependency of useEffect
would re-create the timeout in this case, causing unwanted behavior – although it would be a fix in many similar use cases.
Can I pass count
by reference somehow without the boilerplate of wrapping it in an object and needing to keep that object in sync?
2
Answers
You can use a
useRef
hook to grab the current value ofcount
when your timer runs out.@Caleth included an approach with using just
useRef
in his original answer. As I pointed out, that will not trigger re-render. To fix that, a dummy state can be introduced. Then just oneuseEffect
is required.