javascriptreactjsreact-hooksreact-state

Not updating state variable which is array of objects


perms is an array of objects. Each object is:

{   
  catg: 'Catg1' 
  serv: ->array of serv objects     
    {   
      checked: false,
      desc: 'Serv 1'    
    } 
}

On button click, based on the catg sent, I need to update all checked to true for each serv object for that catg. Code is as below:

const [perms, setPerms] = useState<any[] | null>(null);

const handleClick = (
  event: React.MouseEvent,
  catg: string
) => {
  const permCopy = [...perms];
  const catgPerm = permCopy?.filter(function (perm) {
    return perm.catg === catg;
  });
  const catgServs = perms?.servs;
  catgServs.map((serv, index) => {
    const updatedServ = { ...serv, checked: true };
    catgServs[index] = updatedSer;
  });
  }
  setPerms(permCopy);

The screen is not re-rendering and it does not reflect the updated check boxes. I suspect the state variable is not getting updated.

The data looks like this:

perms = [
  {
    catg: 'Catg1',
    servs: [
      { 
        desc: 'Serv1',
        checked: false,
      },
      { 
        desc: 'Serv2',
        checked: false,
      },
      { 
        desc: 'Serv3',
        checked: false,
      }
    ]
  },
  {
    catg: 'Catg2',
    servs: [
      { 
        desc: 'Serv4',
        checked: false,
      },
      { 
        desc: 'Serv5',
        checked: false,
      },
      { 
        desc: 'Serv6',
        checked: false,
      }
    ]
  }
]

Solution

  • Issues

    // Shallow copy of perms array
    const permCopy = [...perms];
    
    // Filtered copy of permCopy, never referenced again
    const catgPerm = permCopy?.filter(function (perm) {
      return perm.catg === catg;
    });
    
    // Direct reference to whatever perms.servs is
    // Maybe this was supposed to be `catgPerm[0]?.servs` 🤷🏻‍♂️
    const catgServs = perms?.servs;
    
    // Mutations in mapping callback
    catgServs.map((serv, index) => {
      const updatedServ = { ...serv, checked: true };
      catgServs[index] = updatedSer;
    });
    
    // Save the shallow copy of perms array back into state
    setPerms(permCopy);
    

    Solution Suggestion

    interface Serv {
      checked: boolean;
      desc: string;
    }
    
    interface Perm {
      catg: string;
      servs: Serv[];
    }
    
    const [perms, setPerms] = useState<Perm[] | null>(null);
    
    const handleClick = (
      event: React.MouseEvent,
      catg: string
    ) => {
      setPerms(perms => perms.map(           // <-- shallow copy perms array
        perm => perm.catg === catg
          ? {                                // <-- new "Perm" object reference
            ...perm,
            servs: perm.servs.map(serv => ({ // <-- shallow copy servs array
              ...serv,                       // <-- new "Serv" object reference
              checked: true
            }))
          }
          : perm                             // <-- non-updated "Perm" object
      ));
    }