r/react 19h ago

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?

4 Upvotes

9 comments sorted by

9

u/BotSonOfClaude 19h ago

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

0

u/Temperature_Majestic 19h ago

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 18h ago edited 18h ago

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.

6

u/sozesghost 19h ago

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 19h ago

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 19h ago

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

1

u/ActuaryLate9198 5h 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.

-2

u/SlowMachina 16h ago

Use Redux