0

So as you probably know, in normal mode, we use update dependencies to get notice when the state updated, like this:

const [val, setVal] = useState();


useEffect(() => {}, [val]]);

But in my case, I have an array in my state and I'm trying to update it in a loop in my useEffect like this:

const [val, setVal ] = useState([...]);

useEffect(() => {
   anotherArr.forEach(i => {
      // get val and modify some indexes 
      setVal(modifiedValuesArray);
   }
}, []);

In this case, every time forEach loop runs, I'm getting the initial val (I know because val is not a dependency of useEffect) but if I put it as a dependency, it will update twice. what is the solution for this?

EDIT: Basically, I mean when I update state in a round of loop in useEffect, on the next round, I'm not getting the updated state but the initial state before entering the loop. And I know, that is because of the nature of useEffect which gives us a memorized value of state (since we didn't pass it as a dependency to avoid the additional execution), but what is the solution in these types of scenarios.

Pouya Jabbarisani
  • 1,084
  • 3
  • 16
  • 29
  • 1
    You should `setVal` only once. Calculate the transformed array, then set it. Is there any significant reason for using `useEffect` in that scenario specifically? – briosheje Nov 23 '20 at 13:11
  • @briosheje actually the real code is much complex than this basic example. but, yeah I need to use useEffect to get notice when one of the required dependencies updates.(I want to update indexed one by one for some reasons) – Pouya Jabbarisani Nov 23 '20 at 13:13
  • 1
    @PouyaJabbarisani why not send an arrow function inside `setVal` which would have the previous state as a param ? – usafder Nov 23 '20 at 13:18

3 Answers3

0

Use setVal only once, not during an forEach loop. setState is async so you can't depend on it like its synchronous. In your example setVal will actually be executed some time in the future.. Do you maybe have a codesandbox example?

EDIT: You don't get updated state on "next round". setState will be executed N times, and will put it in an update queue, and React will probably only update the last setState value for optimisation. Also, your example useEffect will run only once..

zhisme
  • 2,368
  • 2
  • 19
  • 28
ahuzej
  • 34
  • 1
  • 4
0

I came across this answer:(https://stackoverflow.com/a/59422750/2728431) and it solved my problem.

for getting updated state, (as @usafder said in a comment), we need to pass state as a value in an arrow function just like this:

const [val, setVal ] = useState([...]);

useEffect(() => {
   anotherArr.forEach(i => {
      setVal(val => {
         // modify based on provided val on arrow function
         return modifiedValuesArray
      });
   }

}, []);
Pouya Jabbarisani
  • 1,084
  • 3
  • 16
  • 29
  • Correct. There is also a library that allow you to use hook with callback. If you are interested [here](https://www.npmjs.com/package/use-state-with-callback) npm link. – Giovanni Esposito Nov 23 '20 at 13:49
0

Whenever you need to update the state using its current value, you need to send in a function instead to the state setter which would give you the updated current value of the state as a param. So in your case it would be something like below:

const [val, setVal ] = useState([...]);

useEffect(() => {
  anotherArr.forEach(i => {
    setVal((currVal) => {
      let modifiedValuesArray = [];
      // your update logic here (use currVal instead of val)
      return modifiedValuesArray;
    });
  });
}, []);
usafder
  • 791
  • 5
  • 17