Always Update State Through prev!

Thu, June 12, 2025 - 2 min read
React developer updating state through prev

⚛️ Always Update State Through prev — Future You Will Thank You

It is tempting to write setCount(count + 1) and move on. Until a race pops up. Multiple updates in a row, async effects, React 18 batching — and suddenly an older value overwrites the new one. Building the habit of setState(prev => prev + 1) prevents more bugs than you expect.


Ten reasons to favor setState(prev => …)

  1. Sequential updates stay consistent. Multiple setState calls in one handler all see the latest value.
  2. Safe with React 18 batching. Even if React defers rendering, prev points to the newest state, not a stale snapshot.
  3. Async-friendly. Timers, fetch, or await cannot accidentally revert state that another event already changed.
  4. Works with transitions and deferred values. prev keeps truth even when renders happen later.
  5. Fewer effect loops. When effects update state, the callback form avoids infinite toggles and duplicates.
  6. Clean analytics counters. Rapid clicks on “like” do not lose increments when you rely on callback updates.
  7. Predictable undo/redo stacks. Sequential history updates add entries without gaps.
  8. Safer structural changes. Updating objects or arrays from prev keeps immutability explicit.
  9. Concurrent-mode ready. Future React may re-render components multiple times; callbacks always compute from the latest state.
  10. Readable intent. Future maintainers immediately see that the new value depends on the previous one.

Make prev your default reflex

  • Swap the basics: turn setValue(value + 1) into setValue(prev => prev + 1) everywhere — even when bugs seem unlikely.
  • Handle complex structures: setFilters(prev => ({ ...prev, page: prev.page + 1 })) keeps updates immutable.
  • Enforce with linting: add custom ESLint rules that flag direct state math inside setters.
  • Test the pattern: write unit tests that trigger consecutive updates to prove values climb correctly.
  • Document the rule: add a note to your contributing guide explaining why callback updates are mandatory.

A bug factory without prev

const [likes, setLikes] = useState(0);
 
const handleLike = () => {
  setLikes(prev => prev + 1);
  sendAnalyticsEvent('like_clicked');
};

Switch it back to setLikes(likes + 1) and fast taps or deferred renders will quietly drop increments. The callback keeps every action accounted for.


Takeaway

Callback state updates are cheap insurance against subtle failures. Spend a few minutes refactoring today and tomorrow metrics, QA, and future-you will be happier. Make prev second nature and your React components stay trustworthy. 💪