Home > Software engineering >  Updating useState object values with map
Updating useState object values with map

Time:04-23

Im using useState inside of useContext, I have a button inside of a component that when the user clicks the button it will add a new element on the page which in return it will create a new object in the useState array like so:

  const [items, setItems] = useState([])

  const addItem = () => {
    setItems((items) => [...items, {
      id: items.length,
      key: randomKey(),
      selected: false,
      FG_value: 0,
      FS_value: 0,
      FB_value: 'auto',
      AS_value: 'auto',
      OR_value: 0,
    }])
  }

Now when a user clicks on an element on the page, it will toggle the "selected" value false/true like this:

  const editItem = (value) => {
    const selectedItem = items.map((item) => item.key === value ? {
      ...item,
      selected: !item.selected
    } : item)
    setItems(selectedItem)
  }

The above code works like it should, but where im getting stuck is that when a user clicks on an element and then clicks to a different element, both elements "selected" values are true. I'm trying to correctly write the code so that if an element is clicked and the item.key === value, then toggle the "selected" and set all other items.selected values to false. So basically if the user clicks the first element, that one is selected, then if they click a different element, it deselects the first element then selects the new clicked element.

CodePudding user response:

I'd suggest, rather than structuring your state such that each item has a selected property, create a separate state for the selected item. Then it's as simple as setting that state to the selected item's key or index.

If the items don't get removed from the array, you could do:

const [items, setItems] = useState([])
const [selectedItemIndex, setSelectedItemIndex] = useState(-1);
// ...
{items.map((item, i) => (
  <other item JSX>
  <button onClick={() => setSelectedItemIndex(i === selectedItemIndex ? -1 : i)}
))}

Now, wherever you use item.selected currently, instead compare the index of the item being iterated over against the selectedItemIndex.

CodePudding user response:

Like CertainPerformance, I would keep separate state for the selected item. However, if you wanted to modify your editItem function to toggle one item (from selected to unselected or unselected to selected) and make all other items unselected (i.e. selected is false), then you can write it like this:

const editItem = (value) => {
  const updatedItems = items.map((item) => item.key === value ? {
    ...item,
    selected: !item.selected
  } : {
    ...item,
    selected: false
  })
  setItems(updatedItems)
}

I renamed selectedItem to updatedItems because selectedItem made it seem like the variable was only for one item when it was actually for all items.

Let me know if it helps.

  • Related