theotheo
theotheo

Reputation: 155

How to set a state in a forEach loop using useState hook

I want to transfer data in an array A into a object B, so i doing something like [array].forEach(e => setB({...B, e})), but it seems like when traversing on later elements, former actions have been "forgotten", how should i achieve my goal?
Prototype should looks like this:

import React from "react"
import {Map} from "immutable"

export default function App() {
    const [arrayA, setA] = React.useState([1, 2]);
    const [objB, setB] = React.useState(Map({}));
    React.useEffect(() => {
        arrayA.forEach(num => {
            setB(objB.set(num, num*num));
        })
    }, [])

    return (<div>null</div>)
}

Thanks in advance!

Upvotes: 3

Views: 10615

Answers (2)

Drew Reese
Drew Reese

Reputation: 202638

You are correct. With a regular value state update in a loop each subsequent enqueued update overwrites the previous update, so the last update is the one used for the next render cycle.

Solution

Normally you would use a functional state update to update from the previous state, not the state from the previous render cycle. In this case it makes more sense to map the arrayA state to an array of key-value pairs and do a single update for the objB state.

React.useEffect(() => {
  setB(Map(arrayA.map(num => [num, num * num]));
}, []);

Upvotes: 3

Taghi Khavari
Taghi Khavari

Reputation: 6582

You're getting stale data and you should use setState callback like this:

export default function App() {
  const [arrayA, setA] = React.useState([1, 2]);
  const [objB, setB] = React.useState(Map({}));
  React.useEffect(() => {
    arrayA.forEach((num) => {
      setB(oldValue => oldValue.set(num, num * num));
    });
  }, []);

  return <div>null</div>;
}

Upvotes: 1

Related Questions