import React, { useState} from "react";
import ReactDOM from "react-dom";
function App() {
const [count, setCount] = useState(0);
function handleAlertClick(){
return (setTimeout(() => {
alert("You clicked on: " + count);
}, 3000))
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
<button onClick={handleAlertClick}>Show alert</button>
</div>
);
}
I just want to know if this works the way that I think it does, or if there is a better explanation!
Whenever the setState
method is called the state gets a new reference. This means the original state doesn't have a new value, but we instead create a new state with a new value. When we click on the second button, the event handler function captures the reference of the original state. Even if we click the first button many times, when the alert is displayed it will show the value of the state that the event handler captured its reference.
Is this correct?
Digging into the classic problemThe most classic problem is the closure trap of React Hooks. Some friends may have encountered similar problems, but they may not understand this problem from the underlying principles of React.
Hooks ease the management of state and side effects inside functional React components. Moreover, repeated logic can be extracted into a custom hook to reuse across the application. Hooks heavily rely on JavaScript closures. That's why hooks are so expressive and simple.
Yes. When useState is called, it gets the next available index using idx and stores it in the constant _idx . The setState function returned forms a closure because of which it remembers the _idx corresponding to its state even though useState has finished execution.
The reason the alert
shows the outdated value of count
is because the callback passed to setTimeout
is referencing an outdated value of count
captured by the closure. This is usually referred to as a stale-closure.
On the initial render, the anonymous function passed as a callback to setTimeout
captures the value of count
as 0
, and when the button show alert
gets clicked the callback gets queued but with the outdated value of count.
In the case above the easiest solution to show the updated value of count in the alert message and fix the stale-closure issue will be to use a ref
.
function App() {
const [count, setCount] = useState(0);
const latestValue = useRef(count);
const handleAlertClick = () => {
setTimeout(() => {
alert(`count is: ${latestValue.current}`);
}, 3000);
};
return (
<div>
<p>You clicked {count} times</p>
<button
onClick={() => {
setCount(prev => {
latestValue.current = prev + 1;
return prev + 1;
});
}}
>
Click me
</button>
<button onClick={handleAlertClick}>Show alert</button>
</div>
);
}
Working demo in codesandbox
Hooks rely heavily on closures to work, so it very likely that you may bump into problems regarding stale-closures. Here is a nice article on how stale-closures create issues when using react-hooks and demonstrates how to fix some the issue in some situations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With