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 setTimeout
s point of view.