I am aware of the issue of stale state in nested functions or callbacks (e.g. setTimeout) inside a React component. For example, in the code below (take from React docs) we see the issue of stale state:
"If you first click “Show alert” and then increment the counter, the alert will show the count variable at the time you clicked the “Show alert” button."
function Example() {
const [count, setCount] = useState(0);
function handleAlertClick() {
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 know that we can create a mutable reference to the most up to date/current value of state by using useRef:
function Example() {
const [count, setCount] = useState(0);
const stateRef = useRef()
stateRef.current = count
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + stateRef.current);
}, 3000);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<button onClick={handleAlertClick}>
Show alert
</button>
</div>
);
}
My question is, why does creating a reference to our useRef variable (stateRef) solve the problem of stale state, and why does this get around the issue of closure? Surely by just referencing count directly inside setTimeout, once the component re-renders the count value will have updated, and therefore our reference to it would return the up to date value.
I am struggling to understand why referencing stateRef as opposed to count gets around the issue of stale state, given they are both declared in the same lexical scope from setTimeouts point of view.