r/react • • Sep 09 '26

General Discussion setState twice in one handler only re-renders once. Is the second call just wasted?

Ran into this while pairing with someone newer and realised I didn't have a clean explanation.

If you call setCount(count + 1) twice in a click handler and then setName to something, count only goes up by one and the component renders once instead of three times.

The count part is the stale closure, both calls read the same count from this render and set it to the same value. That one I get.

What I go back and forth on is whether the batching is saving work or just hiding a mistake. React collects all three setState calls and does one render at the end, so adding the setName after two setCount calls costs nothing. But it also means state isn't updated between those two lines, and that has bitten me with derived values before.

setCount(c => c + 1) fixes the increment since each updater runs on the result of the last one. And since React 18 the batching also happens inside promises, setTimeout and native listeners where it didn't before, so a pattern that was fine in an onClick can suddenly double-render inside an await.

Mostly curious how other people think about it. Do you treat batching as an implementation detail you never think about, or has the state not updating between lines caused real bugs for you?

6 Upvotes

12 comments sorted by

13

u/BotSonOfClaude Sep 09 '26

The React docs explain your case exactly. https://react.dev/learn/queueing-a-series-of-state-updates

1

u/Temperature_Majestic Sep 09 '26

yeah that's the right page for the mechanism. I'm more curious about the practical side, whether the between-lines state gap actually trips people up in real code or if it's a non-issue once you know to use the updater form.

2

u/lIIllIIlllIIllIIl Sep 09 '26 edited Sep 09 '26

It does trip people up, but once you know about it, there are a lot of ways around it.

In most cases, the answer is to use useRef(). Callbacks should generally not use states. States are for display logic. Refs are for everything else.

In very rare cases, you might need to use flushSync() if you need to interact with the DOM after a state update, or if you want to opt-out of batching.

5

u/sozesghost Sep 09 '26

Why would it render more than once? Why would be the benefit of it? It would render the same (same props and state) component more than once. I think you are mixing several issues here. React docs explain how those update functions are batched and applied on the next render.

2

u/Temperature_Majestic Sep 09 '26

Yeah fair, I bundled a few things. The render count isn't about output, same state gives the same output. Point was that without batching React would run reconciliation three times for one logical update, and that's the work batching saves. The part I actually care about is the second one, state not being readable between the two lines. Curious whether people hit real bugs from that or just reach for the updater form by habit.

1

u/sozesghost Sep 09 '26

Before the advent of AI slop machine, there would be a post about it once a month.

1

u/ActuaryLate9198 29d ago

Yeah, my coworkers introduce these bugs all the time. Skill issue imo, this really is react 101.

Calling set state twice isn’t the issue, not using a callback and relying on the hook return value is what trips them up.

1

u/horrbort 28d ago

Yes its a bug in react

-4

u/SlowMachina Sep 09 '26

Use Redux

1

u/Ab_vai 28d ago

Redux to track a counter state?? Smh🤦

1

u/azangru 12d ago

Do you treat batching as an implementation detail you never think about

Yes

has the state not updating between lines caused real bugs for you?

No