1

In the React docs (https://reactjs.org/docs/hooks-reference.html#usestate) it says "If the new state is computed using the previous state, you can pass a function to setState" like so:

const [count, setCount] = useState(initialCount);
setCount(prevCount => prevCount + 1)

But why is React suggesting this as a solution when it can be done more succinctly using the count variable like so:

const [count, setCount] = useState(initialCount);
setCount(count + 1)

This latter approach works even when working with mutable objects like arrays as in this example:

https://codesandbox.io/s/6b-array-subcomp-event-usestate-props-r032xv

skyboyer
  • 22,209
  • 7
  • 57
  • 64
Luke
  • 2,751
  • 1
  • 17
  • 20

2 Answers2

2

If you have a function that use SetCount twice, without using the prev state, it will setCount only once.

const [count, setCount] = useState(0);
const fun1 = () =>{
   setCount(count + 1)
   setCount(count + 1)
}
// it will change count to 1 rather than 2

That's why it's recommended to use prev state

Moiz Sheikh
  • 155
  • 8
  • Thanks. This is really helpful. And such a simple reason why the prev state is needed. It begs the question why this explanation is missing in the docs. I guess that's why we have Stackoverflow! – Luke Apr 20 '22 at 22:31
  • 1
    @Luke I watched the course videos of webdevSimplified and concepts like this were really well explained – Moiz Sheikh Apr 20 '22 at 23:17
  • Good to know. I'll keep webdevsimplified in mind. Perhaps you could answer a questions related to this. Why is useState designed this way? Wouldn't it make more sense for count to update directly after setCount so that if setCount is called again in the same block of code it has access to the new count? Or is there something in React's design/philosophy that makes this a bad idea? – Luke Apr 21 '22 at 02:12
  • This post is related (albeit not a duplicate IMO): https://stackoverflow.com/questions/62979007/react-usestate-update-state-value-vs-function?rq=1 – Luke Apr 21 '22 at 02:34
1

The suggestion to use a function is because you're assured to get a reliable previous state in its parameter. On the other side, if you are using a value of a state directly it might happen that you are reading its stale value as react's setState operation is asynchronous and might still have not updated the state.

Here is an example:

function App() {
  const [a, setA] = useState(0);
  const [b, setB] = useState(0);
  useEffect(() => {
    setA(a + 2);
    setA(a + 2);
  }, []);
  useEffect(() => {
    setB((prev) => prev + 2);
    setB((prev) => prev + 2);
  }, []);
  return (
    <div>
      <h1>{a}</h1>
      <h1>{b}</h1> 
    </div>
  );
}

a will contain 2 because react will do both setA at once (batch update) therefore reading previous state a once, which is 0 at that moment.

b, however, will have the correct value, 4, as it explicitly uses the previous state provided by the updating functions parameter.

2jt
  • 93
  • 5
  • Thanks. This answer was helpful to me too. Oddly the second useeffect is rendering 8 instead of 4. Not sure why that is happening: https://codesandbox.io/s/sharp-night-cij6z2?file=/src/App.js – Luke Apr 20 '22 at 22:34
  • 1
    @Luke, rendering 8 instead of 4 only happens in development mode (read here: https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects). If you build the project for production, it will render 4 as expected. – 2jt Apr 21 '22 at 07:16
  • 1
    @Luke, or you can also change `StrictMode` in your codesandbox example with `div` to see the correct execution (this way you don't have to build the project for production). – 2jt Apr 21 '22 at 09:44