The Forge LogoThe Forge Logo

© 2026 Youssef Elmohamadi The Forge. All rights reserved.

ON THIS PAGE

Understanding State Snapshots & Batching in React

1 month ago
5 min read
55
Share Article:

Introduction

If you're a React developer, you've probably run into this classic example:

javascript
1const [count, setCount] = useState(0);
2function handleClick() {
3setCount(count + 1);
4setCount(count + 1);
5setCount(count + 1);
6console.log(count); 
7// Logs 0, not 3! 
8}

So... is React delaying the state update? Is useState asynchronous? Or is something deeper happening? The real answer is understanding two of React's most important concepts: State as a Snapshot Batching React collects all state updates into a queue and processes them before the next render instead of rendering immediately after every setState.

What is a State Snapshot?

A snapshot is one of the reasons React is so fast. Every time React renders a component, it creates a snapshot of that render. That snapshot contains the current values of the component's state, props, context, and the event handlers created during that render. This means that every event handler "remembers" the state values that existed when that render happened. That's why inside the same event handler, count never changes, even if you call setCount() multiple times.

javascript
1const [count, setCount] = useState(0);
2function handleClick() { 
3setCount(count + 1);
4 console.log(count); 
5// Still 0
6 }

React doesn't modify the current snapshot. Instead, it schedules another render with a new snapshot. This snapshot system is one of the biggest reasons React performs so well in large applications because React always works with a consistent view of your data during each render.

Why doesn't React update the UI immediately?

At first, it might sound logical for React to update the screen every time you call setState. But that would create another problem called Half-Finished Renders. Imagine you have:

javascript
1const [firstName, setFirstName] = useState("Ahmed");
2const [lastName, setLastName] = useState("Ali");

The UI currently shows:

javascript
1//Ahmed Ali

Now you want to update both values:

javascript
1setFirstName("Youssef"); 
2setLastName("Mohammed");

If React rendered after every single update, the UI could briefly become:

javascript
1//Youssef Ali

The first name is updated, but the last name is still the old one. Then another render happens:

javascript
1//Youssef Mohammed

For a brief moment, the UI displayed inconsistent data. That's exactly what React wants to avoid. Instead, React waits until the entire event handler finishes, batches all state updates together, and performs a single render. So the UI goes directly from:

javascript
1//Ahmed Ali

to:

javascript
1//Youssef Mohammed

without ever showing an incomplete state. This is one of the biggest benefits of Batching.

When does React batch updates?

React batches state updates that happen during the same event, like: A button click An input change Any React event handler For example:

Each click is treated as a separate event. React never batches different user interactions together because every interaction should be processed independently.

Updating the same state multiple times

Sometimes you'll need to update the same state several times before the next render. For example:

javascript
1setNumber(number + 1);
2setNumber(number + 1);
3setNumber(number + 1);

Most people expect the result to be 3. But it actually becomes 1. Why? Because every call reads the exact same number value from the current snapshot. If number is 0, React receives:

javascript
1setNumber(1);
2setNumber(1);
3setNumber(1);

So the final value is simply:

javascript
1//1

The solution: Updater Functions

React provides another way to update state:

javascript
1setNumber(n => n + 1);
2setNumber(n => n + 1);
3setNumber(n => n + 1);

Instead of passing the next value directly, you pass a function that tells React how to calculate the next state from the previous one. React places these updater functions into its queue. After the event handler finishes, React executes them one by one:

javascript
1//0 → 1
2//1 → 2
3//2 → 3

Now the final value becomes:

javascript
1//3

The difference

When you pass a value:

javascript
1setCount(5);

React simply replaces the state with that value. When you pass a function:

javascript
1setCount(prev => prev + 1);

React uses the latest available state while processing the queue. That's why updater functions are the correct solution whenever the next state depends on the previous one.

Final Thoughts

React isn't "slow" or simply "asynchronous." It intentionally delays rendering to keep your UI consistent and avoid unnecessary work. Understanding State Snapshots and Batching explains why: console.log() still prints the old value. Multiple setState() calls don't immediately update the state. Functional updates (prev => ...) work correctly. React can render large applications efficiently while keeping the UI consistent. Once you understand these two concepts, many React behaviors that seem confusing suddenly make perfect sense.

Enjoyed the read? Share it!

Share Article: