1

I am learning React and I have a question. Sometimes we use the previous State when we want to set a new value:

import React, { useState } from "react";

function HookCounter() {
  const initialCount = 0;
  const [count, setCount] = useState(initialCount);
  return (
    <div>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount((prevState) => prevState + 1)}>
        Increment
      </button>
      <button onClick={() => setCount((prevState) => prevState - 1)}>
        Decrement
      </button>
    </div>
  );
}

export default HookCounter;

This is because we are building the new state on top of the previous state.

Does that mean that if we use the spread operator (for example in an array or an object), we don't need to use the previous State? Because spread gives us the previous State already?

For example

import React, { useState } from "react";

function ArrayCounter() {
  const [items, setItems] = useState([]);

  const addItem = () => {
    setItems([
      ...items,
      {
        id: items.length,
        value: Math.floor(Math.random() * 10) + 1,
      },
    ]);
  };
  return (
    <div>
      <button onClick={addItem}>Add a number</button>
      <ul>
        {items.map((item) => (
          <li key={item.id}>{item.value}</li>
        ))}
      </ul>
    </div>
  );
}

export default ArrayCounter;

Thank you!

josegp
  • 499
  • 6
  • 21
  • Thank you @Phil for sharing and apologies. I read it but couldn't really understand what it has to do with my question. Could you elaborate what is the relationship to my question? Many thanks! – josegp Mar 21 '23 at 02:18
  • The answer is the same. If you're updating state based on the current value, you should always use the [functional update](https://legacy.reactjs.org/docs/hooks-reference.html#functional-updates) form. You can still use the spread operator, eg `setItems((prev) => [...prev, { new: stuff }])`. Without it, you run the risk of missing state updates – Phil Mar 21 '23 at 02:21
  • You can try it yourself using the example in your question. Try hitting the `addItem` button quickly and you'll see that it's not getting every addition – Phil Mar 21 '23 at 02:24
  • Here's a crude example showing the differences ~ https://codesandbox.io/s/strange-sid-pf2swy?file=/src/App.js. Mash each of the buttons quickly and you'll see the issue with not using functional updates. This is all very well articulated by [this answer](https://stackoverflow.com/a/73115899/283366) in the duplicate – Phil Mar 21 '23 at 02:31
  • @Phil thank you!! Then I was wrong thinking that `spread` and `prevState` were doing the same thing right? Even if I use `spread`, I will need `prevState` if my change is based on the previous state – josegp Mar 21 '23 at 02:38
  • @Phil please copy-paste what you wrote in the comment as your answer. Then I can accept it as the correct answer for this question! – josegp Mar 21 '23 at 02:40
  • 1
    No, this is already answered by the duplicate. You can accept that action instead – Phil Mar 21 '23 at 02:42
  • Here is anotherr codesandbox by Phil showing a great example of how `useEffect` will set duplicate itemps when running twice because of strict mode using `setTasks((prev) => [newTasks, ...prev]);` however it won't with `setTasks( [newTasks, ...tasks]);` https://codesandbox.io/s/angry-scooby-rxdh0o?file=/src/App.js:1052-1092 – Ahmed Sbai Mar 21 '23 at 02:44
  • @AhmedSbai I'm not sure how that's relevant. There are no effect hooks in this question – Phil Mar 21 '23 at 02:45
  • still a good example to understand the difference between update based on `prev` and update based on state – Ahmed Sbai Mar 21 '23 at 02:46
  • @AhmedSbai but `prev` _is_ state. Using the functional update just queues the update instead of potentially using stale state – Phil Mar 21 '23 at 03:06
  • Yes you are right I mean the stale state – Ahmed Sbai Mar 21 '23 at 03:08

1 Answers1

0

In an object literal, the spread (...) syntax enumerates the properties of an object and adds the key-value pairs to the object being created.

With the spread operator you're creating a copy by value of the object items.

Does that mean that if we use the spread operator (for example in an array or an object), we don't need to use the previous State? Because spread gives us the previous State already?

Yes, if the state have not been updated by any previous calls.

For more information of the spread operator please read: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

Luis Lara
  • 44
  • 7
  • 2
    _"so you're passing the previous state"_... that's not exactly correct. You're passing the value of state at the time you're calling the setter. This may be within the same render cycle and will not have been updated by any previous calls. See the example in the comments above – Phil Mar 21 '23 at 02:33
  • 1
    Totally agreed with the above. Let me updated. – Luis Lara Mar 21 '23 at 02:37