Problem

We usually try to keep these 🍦 JavaScript, but here’s one for you React devs this week.

import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 1);
        setNumber(number + 1);
        setNumber(number + 1);
      }}>Increment</button>
    </>
  )
}
Solution
import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 1);
        setNumber(number + 1);
        setNumber(number + 1);
      }}>Increment</button>
    </>
  )
}

You might expect that clicking the button would increment our number state by 3, but that’s not how it works. In short, JavaScript creates a closure around the number variables so it’s as if we’re doing

setNumber(0 + 1);
setNumber(0 + 1);
setNumber(0 + 1);

which of course will always set number to 1.

Instead, if we want to update the same state multiple times before the next render, we can pass a function to our setter function like so -

setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);

Now React will add all these functions to a queue, and during the next render, React goes through the queue passing the result from the previous item in the queue to the next, eventually leading to a number of 3.

0 + 1 = 1
1 + 1 = 2
2 + 1 = 3