As mentioned in the React documentation, React takes a snapshot of the UI using its current state at the time of render. Any state change would account to a new re-render.
So an alert timed to be executed some time after and printing a state variable, prints that value of the state variable when the alert is initially scheduled to call. But when the same alert is scheduled to printing a reference variable, it prints the value of the reference variable which is present at that instantaneous time. Here is the code :
import { useState, useRef } from 'react';
export default function Chat() {
const [text, setText] = useState('');
const dummy = useRef("");
dummy.current = text;
function handleSend() {
setTimeout(() => {
alert('Sending: ' + text);
}, 5000);
}
return (
<>
<input
value={text}
onChange={(e)=>setText(e.target.value)}
/>
<button
onClick={handleSend}>
Send
</button>
</>
);
}
In this code when the input is changed after clicking Send button, the alert shows the value of the text which was initially just before pressing Send, as evident from the fact that React takes snapshot. But when we replace text in alert by dummy.current, the alert prints the final value of the input just before the alert gets triggered. How does React schedules and substitutes value for timeout functions and how the substitution is different for state variables and reference variables?
If I'm reading your question correctly, you are probably unfamiliar with the concept of Closures. And the behavior you're seeing here is not bound to React but to JS itself.
In JS, functions form closures: any local variables that were in-scope at the time the closure was created will be copied and its copy lives on in the closure. This is why your setTimeout handler can display the "text" variable from your "Chat" component even after the completion of "Chat"'s execution.
The reason "text" and "dummy.current" behave differently is because "text" is a string primitive and "dummy" is a reference.
When captured by a closure, primitives and references alike will be copied into the closure but! References POINT to data. Primitives HOLD the data.
When the setTimeout handler access the "text" variable, it accesses the primitive data that was captured when "handleSend" was called. When the setTimeout handler accesses the "dummy.current" variable, it accesses the reference of this object, which is a memory address that points the primitive data of the text.
In the diagram below, i present the follwing scenario: I enter the word "DOG" and press send. Immediately after I enter the word "CATS" and press send. Then the two "setTimeout" will trigger 5 seconds after their initialisation.
When using "TEXT", as the closure captures the primtive string, the first alert will show "DOG" then the second alert will show "CATS".
When using "DUMMY.CURRENT", as the closure captures the ref of the string, both alterts will lookup what is the value of what the reference points to and display it, in this instance: "CATS."
I hope it made things clearer.