DEV Community

Cover image for 🚀 Day 5 of Learning React: Understanding Automatic Batching & Functional Updates
Bismay.exe
Bismay.exe

Posted on • Edited on

🚀 Day 5 of Learning React: Understanding Automatic Batching & Functional Updates

Expanded update grouping since React 18

📌 Missed Day 4? I explored what React Hooks are, why useState() returns two values, how React remembers state, and why calling the setter function triggers a re-render. You can read it here and then come back. I'll wait. ☕️


Yesterday, I learned that calling setState() tells React to update the UI.

So naturally, I assumed something like this:

setCount(1);
setName("Dev");
setAge(24);
Enter fullscreen mode Exit fullscreen mode

would make React render...

Three times.

Turns out, I was completely wrong.

Today I learned that React is much smarter than that.

Instead of rendering after every state update, React groups multiple updates together and performs only one render.

This optimization is called Automatic Batching.

And honestly...

Once I understood batching, a lot of React's behavior finally started making sense.

Let's dive in. 🚀


🧠 Quick Recap

Yesterday's big question was:

How does React know when to update the UI?

Here's what I learned on Day 4:

  • 🎣 Hooks give React components memory.
  • useState() returns the current state and a setter function.
  • 🔄 Calling the setter updates the state and triggers a re-render.
  • 🧠 React skips updates when the new state is the same as the old one.

Today we're answering a different question:

What happens when we call setState() multiple times? 🤔


⚡ React Doesn't Render After Every setState()

Suppose we write:

setCount(1);
setName("Dev");
setAge(24);
Enter fullscreen mode Exit fullscreen mode

My first thought was:

"React must be rendering three separate times."

Nope.

React groups all of those state updates together before rendering.

The flow looks something like this:

Multiple State Updates
        ↓
React Batches Them
        ↓
Single Render
        ↓
Single Commit
Enter fullscreen mode Exit fullscreen mode

Instead of doing unnecessary work, React waits, collects all the updates, and applies them together.

That's what Automatic Batching is all about.

It's one of React's built-in optimizations that helps improve performance.


🤯 The Counter Experiment That Confused Me

After learning about batching, I tried something interesting.

<button
  onClick={() => {
    setCount(count + 1);
    setCount(count + 1);
    setCount(count + 1);
  }}
>
  Increase
</button>
Enter fullscreen mode Exit fullscreen mode

I expected the counter to jump like this:

0 → 3
Enter fullscreen mode Exit fullscreen mode

Instead...

It became:

0 → 1
Enter fullscreen mode Exit fullscreen mode

Wait...

I called setCount() three times.

Why did it only increase once?


🧩 What React Actually Sees

At first, it looked like I was asking React to increase the counter three times.

But that's not what's really happening.

Assume the current value of count is:

0
Enter fullscreen mode Exit fullscreen mode

Every line becomes:

setCount(1);
setCount(1);
setCount(1);
Enter fullscreen mode Exit fullscreen mode

React batches those updates together.

Since every update is trying to set the state to the same value, the final result is simply:

1
Enter fullscreen mode Exit fullscreen mode

That's why the counter only increases once.

This was a really interesting moment because it showed me that setCount(count + 1) isn't saying:

"Increase the count."

It's actually saying:

"Set the count to this calculated value."


🔄 The Mysterious prev

Then our instructor introduced something new:

setCount((prev) => {
  return prev + 1;
});
Enter fullscreen mode Exit fullscreen mode

At first, I wondered...

What exactly is prev?

The simplest way I understand it now is:

prev is the latest state React has available while processing the update queue.

It's basically the current state at that particular moment.


🤯 My Biggest "Wait... What?" Moment

I accidentally wrote this:

setCount((prev) => {
  prev + 1;
});
Enter fullscreen mode Exit fullscreen mode

When I clicked the button...

The counter became:

undefined
Enter fullscreen mode Exit fullscreen mode

🤯

For a second, I thought React was broken.

Turns out...

The problem was mine.


💡 Why Did It Become undefined?

Arrow functions with curly braces don't automatically return a value.

I forgot to write:

return prev + 1;
Enter fullscreen mode Exit fullscreen mode

So React received:

undefined
Enter fullscreen mode Exit fullscreen mode

instead of a number.

The correct version is:

setCount((prev) => {
  return prev + 1;
});
Enter fullscreen mode Exit fullscreen mode

Or, even shorter:

setCount((prev) => prev + 1);
Enter fullscreen mode Exit fullscreen mode

That tiny return completely changed the result.


🚀 Why Functional Updates Work

Now let's use the functional version three times.

setCount((prev) => prev + 1);
setCount((prev) => prev + 1);
setCount((prev) => prev + 1);
Enter fullscreen mode Exit fullscreen mode

This time, the counter actually increases by 3.

Why?

Because React doesn't keep using the original value.

Instead, each updater receives the latest available state.

The process looks like this:

Initial State
count = 0
    │
    ▼
First updater
prev = 0
returns 1
    │
    ▼
count = 1
    │
    ▼
Second updater
prev = 1
returns 2
    │
    ▼
count = 2
    │
    ▼
Third updater
prev = 2
returns 3
    │
    ▼
Final State
count = 3
Enter fullscreen mode Exit fullscreen mode

That was probably my favorite part of today's lesson.

I finally understood why React recommends functional updates whenever the next state depends on the previous one.


📦 React's Update Queue

Another thing I learned today is that React doesn't execute state updates immediately.

Instead, it stores them in something called an Update Queue.

Imagine writing:

setCount(1);
setCount(2);
setCount(3);
Enter fullscreen mode Exit fullscreen mode

React doesn't instantly update the UI after each line.

The flow is more like this:

setState()
      ↓
Update Queue
      ↓
React Processes Updates
      ↓
Render
      ↓
Commit
Enter fullscreen mode Exit fullscreen mode

This update queue is what makes features like batching and functional updates possible.

React collects everything first...

Then processes it efficiently.


💡 My Mental Model

Here's how I visualize today's lesson:

Multiple setState() Calls
          ↓
Stored Inside React's Update Queue
          ↓
React Batches Them Together
          ↓
Processes Every Update
          ↓
Renders Once
          ↓
Browser Shows Updated UI ✨
Enter fullscreen mode Exit fullscreen mode

That flow made today's topic much easier to understand.


💡 My Biggest Takeaways Today

  • ⚡ React automatically batches multiple state updates together.
  • 🎯 Multiple setState() calls don't always mean multiple renders.
  • 🧠 setCount(count + 1) uses the value from the current render, which is why calling it multiple times doesn't keep increasing the count.
  • 🔄 Functional updates receive the latest available state through prev.
  • 📦 React processes state updates through an internal update queue before rendering.

📚 Learning Source

I'm currently learning React through the React Cohort 3.0 by Devendra Dhote at Sheriyans Coding School.

This article isn't a copy of the course.

It's my personal understanding after today's class, rewritten entirely in my own words.

Writing these articles helps me reinforce what I've learned, and hopefully helps other beginners who are on the same journey. 🤝

If I've misunderstood something, I'd genuinely appreciate your corrections in the comments. 😊


🙌 Final Thoughts

Before today's class, I thought every call to setState() immediately changed the state and re-rendered the component.

Now I know that's not how React works.

React first collects state updates, batches them together, and then processes them efficiently.

The biggest surprise for me wasn't batching itself.

It was realizing why the functional update form exists.

I used to think prev was just another parameter.

Now I understand it's React's way of giving each update the latest available state while processing the update queue.

That small detail completely changed how I think about multiple state updates.

Tomorrow's topic is State Lifting, and after today's lesson, I'm curious to see how different components can share the same state.

See you on Day 6! 🚀


💬 What confused you the most when you first learned React's batching behavior? Were you surprised that calling setState(count + 1) three times only updates the counter once, or did functional updates with prev take longer to click?

I'd love to hear your experience in the comments. 😊

If you're following along with this series, you can also find me on GitHub, where I'll be sharing my projects and documenting my progress.

Bismay-exe (Bismay.exe) · GitHub

👋 Hi, I’m Bismay 💻 Developer passionate about building clean, minimal, and elegant apps 🚀 Focused on coding 🌱 Always learning, creating & new ideas - Bismay-exe

favicon github.com

🤖 AI Disclosure: This article is based on my own React learning journey, class notes, code experiments, and understanding. I used ChatGPT to help improve the writing, structure, and readability of this post. I reviewed and verified the technical explanations before publishing, and I take responsibility for everything shared here.

Thanks for reading! 🚀


Top comments (3)

Collapse
 
francistrdev profile image
FrancisTRᴅᴇᴠ (っ◔◡◔)っ

Watching this development unfolds. 👀 Good work!

Collapse
 
nazar-boyko profile image
Nazar Boyko

This clicked for me a lot faster reading your version than it did when I first hit it in real code, so nice writeup. One bit of history that made the name "automatic" finally make sense to me: before React 18, batching only happened inside event handlers, so those same three setCount calls inside a setTimeout or a promise would have re-rendered three separate times. React 18 stretched batching to cover those cases too, and "automatic" is basically the team saying you no longer have to think about where the updates are coming from. Your prev explanation is spot on, and it's the same reason an async callback can read a stale count, the value was captured back at render time. Day 6 state lifting is a fun one, that's where the "why is my state in the wrong place" lessons start.

Collapse
 
sloan profile image
Sloan the DEV Moderator

Hey, this article appears to have been generated with the assistance of ChatGPT or possibly some other AI tool.

We allow our community members to use AI assistance when writing articles as long as they abide by our guidelines. Please review the guidelines and edit your post to add a disclaimer.

Failure to follow these guidelines could result in DEV admin lowering the score of your post, making it less visible to the rest of the community. Or, if upon review we find this post to be particularly harmful, we may decide to unpublish it completely.

We hope you understand and take care to follow our guidelines going forward!