3

I need to create this kind of object when check the checkboxes

{
"targets": [
    {
      "kind": "foo",
      "channelId": "-897",
      "quote": "true",
      "gatewayName": "bbb"
    },{
      "kind": "bar",
      "channelId": "-1123",
      "quote": "true",
      "gatewayName": "aaa"
     }
   ]
}

and I have this snippet for my from

{gateways ? gateways.map((g) => {

            return (
              <Tooltip
                key={g.name}
                content={`${g.name} - ${g.kind}`}
              >

                <Switch
                  key={g.name}
                  id={g.name}
                  label={g.name}
                  name={g.name}
                  value={`{"kind":"${g.kind}","channelId":"${g.channelId}","gatewayName":"${g.name}","quote":"true"}`}
                  labelProps={{
                    className: "text-sm font-normal text-blue-gray-500",
                  }}
                  onChange={handleCheckboxChange}
                />
              </Tooltip>
            )

}) : "Loading..."}

I create the state

  const [checkboxes, setCheckboxes] = useState([]);

I'm trying to create the handleCheckboxChange

 const handleCheckboxChange = (e) => {
    const { name, checked, value } = e.target;

  
    if(checked){
      setCheckboxes(prev => ({ 
        targets: [prev.targets,JSON.parse(value)]
      }))
    }else{
      setCheckboxes(prev => ({ 
        targets: prev.filter(target => target !== JSON.parse(value))
      }))
    }

    console.log(checkboxes)
  };

But when I check the first checkbox I have two element in array, the first is undefined. when I uncheck I get this error

TypeError: prev.filter is not a function

monkeyUser
  • 4,301
  • 7
  • 46
  • 95

2 Answers2

0

The state for the checkboxes is being initialized as an empty array, however when handling the checkbox change with the function handleCheckboxChange() you're setting the state to be an object. So the next time checked is changed, filter will not be available.

if(checked) {
  setCheckboxes(prev => ({ // Returning an OBJECT
    targets: [prev.targets,JSON.parse(value)] 
  }))
}

else { 
                           // also returns an object
  setCheckboxes(prev => ({ // Filter will not be available due to "prev" being an object
    targets: prev.filter(target => target !== JSON.parse(value))
  }))
}

Also instead of doing this:

value={`{"kind":"${g.kind}","channelId":"${g.channelId}","gatewayName":"${g.name}","quote":"true"}`}

You could use object destructuring:

value={ { ...g } }
Victor Santizo
  • 1,145
  • 2
  • 7
  • 16
0

If the input is checked you are returning this to your state :

{ targets: [prev.targets, JSON.parse(value)] }

This is an object so the state is no longer and array that's why you got

TypeError: prev.filter is not a function

When you try to uncheck

However, since your state is initialized as [] the first check action will not find prev.targets so thats why it is undefined

If I understood well, you want to store an array of objects, so why you want your state to be an object with only one preoperty targets that hold an array ? you can simply make it an array and you know it is representing targets :

if (checked) {
  setCheckboxes((prev) => [...prev, JSON.parse(value)]);
} else {
  setCheckboxes((prev) =>
    prev.filter((target) => JSON.stringify(target) !== JSON.stringify(JSON.parse(value)))
  );
}

Note: you have to use JSON.stringify to compare two objects, otherwise the result is always false.


But if you insist, what you have to do is to initilize your state this way :

const [checkboxes, setCheckboxes] = useState({ targets: [] }); // make the property targets exist by default

and your code is fine you only have to change this line :

targets: prev.filter(target => target !== JSON.parse(value)) // prev is an object while prev.target is what you need

with

targets: prev.targets.filter((target) => JSON.stringify(target) !== JSON.stringify(JSON.parse(value)))
Ahmed Sbai
  • 10,695
  • 9
  • 19
  • 38
  • thanks for your answer. so how I need to initialize my state? I need to create that kind of object because the server wants it. your snippet code didn't remove the element when I uncheck – monkeyUser Mar 18 '23 at 21:47
  • I updated my answer, take a look and give me feedbacks – Ahmed Sbai Mar 18 '23 at 22:13
  • thanks for your time. When I select the first element, I get the targets[] with an empty value, when I uncheck I can see what I checked before. If I select another checkbox I have the same behavior. If I select 2 elements I can get targets with one element empty and only one element populated – monkeyUser Mar 18 '23 at 22:19
  • This is because you are trying to `console.log` from the wrong place take a look [here](https://stackoverflow.com/a/75758700/13488990) – Ahmed Sbai Mar 18 '23 at 22:20
  • so if you want to see the new value of `checkboxes` you have to log from the JSX not from inside `handleCheckboxChange` because the value of a `useState` hook does not change immediately when you update it but only when the component rerenders – Ahmed Sbai Mar 18 '23 at 22:30