how to set checkbox in useState and remove duplicate checkbox value in react
Asked Answered
C

3

0

I have two problems with the checkbox

1- When I click on each checkbox, they are correctly added to plantsFill, but duplicate items are also saved. I want it to be deleted if the plant_id is duplicated.

2- When I uncheck it, that item is still stored in my plantsFill and is not deleted

const allPlants = [
  {
    "plant_id": 1,
    "title": "title 1",
  },
  {
    "plant_id": 2,
    "title": "title 2",
  },
  {
    "plant_id": 3,
    "title": "title 3",
  },
];
const handleCheckClick = (e) => {
  setPlantsFill([...plantsFill, {plant_id: e.target.value}]);
};
{allPlants.map((t) => {
  return (
    <div key={t.plant_id}>
      <label htmlFor="">
        <input
          type="checkbox"
          value={t.plant_id}
          onChange={handleCheckClick}
        />
        {t.title}
      </label>
    </div>
  );
})}
Comfy answered 3/1, 2023 at 16:29 Comment(0)
C
1

Your problem is caused by your spreadings, who recreate the same array with a new value without deleting the old one. If you really want to use spread syntax, you will need to deal with sorting, because the new value will be added last in the array (so even if you filter(), your modified checkbox will end up changing it's position on the list). I recommend you to simply use map() to edit them.

You can add a checked property to each of your checkbox, and set their value using event.target.checked or their own checked property, like:

const [allPlants, setPlantFill] = React.useState([{
  "id": 1,
  "title": "title 1",
  checked: false
}, {
  "id": 2,
  "title": "title 2",
  checked: false
}, {
  "id": 3,
  "title": "title 3",
  checked: false
}]);

const handleCheckClick = (id) => {
  setPlantsFill(allPlants.map((checkbox) => checkbox.id === id ? { ...checkbox, checked: !checkbox.checked } : checkbox));
};

return (
  {allPlants?.map((checkbox) => (
    <div key={checlbox.id}>
      <label htmlFor="">
        <input
          type="checkbox"
          value={checkbox.checked}
          onChange={() => handleCheckClick(checkbox.id)}
        />
        {checkbox.title}
      </label>
    </div>
  )) ?? (
    <div>
      Loading...
    </div>
  )}
);
Cohesion answered 4/1, 2023 at 10:3 Comment(2)
correct , but when i want to append this object to formData , from api tell me The plantsId.0.plant_id field is required. | this is my append code : plantsFill.forEach((plantFill) => { formData.append("plantsId[]", plantFill); });Comfy
I simply wrote a clearer version, but you may re-use your architecture, like plant_id instead of my id etc...Cohesion
B
1

The problem is with handle handleCheckClick function, in your code you are concating items instead of updating their value. One more thing onChange is called for checking and unchecking so you need to check (no pan intended) what is the new status to see if you need to add to the array or remove it from the array.

example:

   const handleCheckClick = (e) => {
    if (e.checked) {
        setPlantsFill(previousPlantsFill => [...previousPlantsFill, {plant_id: e.target.value}]);
        return;
    }

    setPlantsFill(previousPlantsFill => previousPlantsFill.filter(plantFill => plantFill.plant_id !== e.target.value));
};

and a more clean and robust example (checking if value already exists before adding, using useCallback, and deconstruct syntax)

const handleCheckClick = useCallback((e) => {
    if (e.checked) {
        setPlantsFill(previousPlantsFill => {
            if (previousPlantsFill.some(({plant_id}) => plant_id === e.target.value)) {
                return previousPlantsFill
            }

            return [...previousPlantsFill, {plant_id: e.target.value}]
        });
        return;
    }

    setPlantsFill(previousPlantsFill => previousPlantsFill.filter(({plant_id}) => plant_id !== e.target.value));
}, []);
Burglary answered 4/1, 2023 at 10:13 Comment(0)
T
1

Assuming allPlants do not change since you define it as a const and you just want to keep track of what plants are being checked. You can keep a single array of all the plant_id's.

const [plantsFill, setPlantsFill] = useState([]);

const handleCheckClick = (e) => {
  const value = e.target.value;
  const hasPlant = e.target.checked;
  setPlantsFill((prevPlantsFill) => {
    // if in the array return the array without the value
    if (hasPlant) return prevPlantsFill.filter((plant) => plant !== value);
    // else return the old array with the new value
    return [...prevPlantsFill, value];
  });
};

If you want to get all the plant objects which are checked you can do it like so

const allCheckedPlants = allPlants.filter((plant) =>
  plantsFill.includes(plant.plant_id)
);

Hope this helps you with your project!

Thesaurus answered 8/1, 2023 at 19:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.