Have you ever tried to log or send state immediately after calling its set function, only to find the old value instead?
const [name, setName] = useState("");
const handleChange = (e) => {
setName(e.target.value);
console.log(name); // β Prints "ahme" instead of "ahmed"
};This is one of the most common "aha!" moments for React developers. It's not a bugβit's a fundamental part of how React's architecture works. Let's break down the "why" and the "how to fix it."
In traditional JavaScript, variables are "live"βyou change them, and they change everywhere. In React, state behaves like a snapshot.
When your component renders, React takes a snapshot of everything: your props, your state, and your event handlers. These values are constant for that specific render.
name is "ahme".handleChange is called.handleChange: The variable name is locked to "ahme".setName("ahmed") is called: React schedules a new render.console.log(name) still sees the "ahme" from the current snapshot.name is "ahmed".Because handleChange was created during Render #1, it "closes over" (remembers) the values from that render. Even though you told React to update the state for the next render, you cannot change the variables in the current execution.
If you need the new value immediately within the same function, the simplest fix is to store the value in a constant first.
const handleChange = (e) => {
const newValue = e.target.value; // 1. Capture the fresh value
setName(newValue); // 2. Schedule update for UI
// 3. Use the fresh value for logic
doSomething(newValue); // β
Correct!
};useEffect HookUse this when you want to perform a "side effect" every time a specific state changes, regardless of where the change came from.
useEffect(() => {
// This runs AFTER the component has re-rendered with the new state
if (name.length > 0) {
console.log("State is now:", name); // β
Correct!
}
}, [name]); // Dependency array: only run when 'name' changesIf your new state depends on the previous state (like a counter), always use a functional update to avoid stale closures.
// β Dangerous if multiple clicks happen fast
setCount(count + 1);
// β
Safe: React gives you the absolute latest pending state
setCount(prev => prev + 1); Understanding this concept is the bridge between "fighting React" and "thinking in React." Once you stop expecting state to behave like a live variable, your code will become much more predictable.
More articles you might find interesting
Discover why using array indices as keys in React can lead to performance issues and bugs, and learn the best practices for proper list rendering.
Learn how to implement recursion in React components to manage nested radio button groups using JavaScript's Map object for efficient state management.
Learn how to streamline your API data handling by efficiently removing null, undefined, and empty values from JavaScript objects using Lodash and recursion.