javascriptreactjstypescript

Changing a value of an object of a state variable array


I have an array of objects as a state variable. I am trying to write a function that changes a field in one of the objects.

interface IItem{
 id: string,
 selected: boolean
}

const [array, setArray] = useState<IItem[]>([])

const toggleItem = (id : string) => {
  const item = array.find(i => i.id === id)
  if (item) {
     const copy = {...item, selected: !!item.selected}
     setArray(array.map(i => i.id === id ? copy : i))
  }
} 

The code works, but it looks too complex for this. Is there a way to simplify? (Note, that the order of the items in the array matters).


Solution

  • If you want to simplify state changes, I would recommend checking out the package Immer. With Immer you get a writeable draft of the state that you can mutate. The code might be as long, be if you're working with even more deeply nested object it is very helpful. I also think in you case it helps a lot with readability.

    import { useState } from "react";
    import { produce } from "immer";
    
    interface IItem {
      id: string;
      selected: boolean;
    }
    
    const [array, setArray] = useState<IItem[]>([]);
    
    const toggleItem = (id: string) => {
      setArray(
        produce((draft) => {
          const item = draft.find((el) => el.id === id);
          item.selected = !item.selected;
        }),
      );
    };
    

    EDIT

    If you don't want to introduce a package for local state, I don't think there's an obvious way to change your code to make it less complex. Here's an alternative at least which I would say is a bit more easy to read.

    interface IItem {
      id: string;
      selected: boolean;
    }
    
    const [array, setArray] = useState<IItem[]>([]);
    
    const toggleItem = (id: string) => {
      setArray((previous) =>
        previous.map((item) => {
          if (item.id === id) {
            return {
              ...item,
              selected: true,
            };
          }
          return item;
        }),
      );
    };