简体   繁体   中英

How to update an array inside a useState with a checkbox

I am trying to create a form where each item checked is pushed or removed from the useState-product[].

What I mean is the following:

After a request a table of Hammers is generated automatically.

<table>
  <thead>
    <tr>
      <th scope="col">Use</th>
      <th scope="col">ID</th>
      <th scope="col">Length</th>
      <th scope="col">Width</th>
      <th scope="col">Weight</th>
    </tr>
  </thead>
  <tbody>
    {response &&
      response.map((element) => (
        <tr key={element._id}>
          <th>
            <input type="checkbox" value={element._id} />
          </th>
          <th>{element._id}</th>
          <th>{element.length}</th>
          <th>{element.width}</th>
          <th>{element.weight}</th>
        </tr>
      ))}
  </tbody>
</table>

And this table is part of a form that will be sent with other information including an array of Hammers and I am saving all this form data in the following useState.

interface Order {
  date: Date,
  hammers: string[],
  purchase_order: number,
}

const [form, setForm] = useState <Order> ({
  date: new Date(),
  hammers: [],
  purchase_order: 0
});

Im updating date and purchase_order this way:

const handleForm = (e: React.ChangeEvent<HTMLInputElement>) => {
  setForm({ ...form, [e.target.id]: e.target.value });
};

So my question is, how do I update the hammer array with the _id each time a hammer is checked or unchecked from the table?

Example hammers[ ]: if 2 are checked

hammers = ["639761fff0bbbac77d77e44d", "6397af36a3ead940becb8805"]

There are two problems you have to solve. The first is setting up your events correctly, and the second is actually updating the array correctly.

When we look at your event handler:

const handleForm = (e: React.ChangeEvent<HTMLInputElement>) => {
  setForm({ ...form, [e.target.id]: e.target.value });
};

We see that you're looking for an ID ( e.target.id ), but looking at your input:

<input type="checkbox" value={element._id} />

There is no id attribute to be found. There's also no way for this function to even fire when the input is checked or unchecked. So let's fix those two issues first:

<input
  id={element._id}
  type="checkbox"
  onChange={handleForm}
/>

You'll notice I didn't bother to add a value to the input, so this is an uncontrolled input.

Now that your HTML (JSX) is correct, we can look at the event handler itself. Your initial state looks like this:

const [form, setForm] = useState <Order> ({
  date: new Date(),
  hammers: [],
  purchase_order: 0
});

So you need to have your function return a similar shape, or your state will get all out of whack once you start checking some boxes. Your current code spreads the existing state object and then adds a new key with the checkbox ID. What you need is to do instead is spread the date and purchase_order values (or update them, if appropriate — you tell me) and then manipulate the hammers value.

Also, the value of a checkbox is just the string 'on' . You should use event.target.checked instead.

So:

const handleForm = (e: React.ChangeEvent<HTMLInputElement>) => {
  const {checked, id} = event.target;
  const {hammers} = form;
  const copyOfHammers = [...hammers];

  if (checked) {
    // add a new id to the array
    copyOfHammers.push(id);
  } else {
    // remove the id from the array
    copyOfHammers.splice(copyOfHammers.indexOf(id), 1);
  }

  return {
    ...form,
    hammers: copyOfHammers,
  };
};

This is example code, but you can modify it as needed. This also isn't the only way to potentially add or remove an item from an array.

What you need is to check if e.target.value is true or false and add or remove the elementId from your array. I suggest handleForm to go like this:

const handleForm = (e: React.ChangeEvent<HTMLInputElement>, elementId) => {
    setForm(form => {
        let newFormState = form
        if (event.target.checked) {
            newFormState.hammers = [...form.hammers, elementId] 
        } else {
             newFormState.hammers = form.hammers.filter(f => f !== elementId)
        }
     return newFormState
};

So basically we are creating a copy of your form, overwriting your form.hammers state according to event.target.checked, and returning the newState to your reducer.

I like to use setForm as a callback function like we did here, which means instead of giving the new state, saying "hey... give me the current state (in this case "form", and with that as callback (noted by the arrow function) I would like to return you the new state.

This can be done with less lines of code, but for clearence I have done it in a more didatic way.

Now... input should receive

<input onClick={(e) => handleForm(e, element._id)} ...and our other props />

because we are passing down the element._id to the function.

Cheers.

 const handleChecked = id => e => {
   if(e.target.checked){
    setForm({ ...form, hammers: [...form.hammers, id] })
  } else{
    setForm({ ...form, hammers: form.hammers.filter(e => e !== id) })
  }};

This should probably do it with few tweaks

const handleForm = (e: React.ChangeEvent<HTMLInputElement>) => {
  if (e.target.checked) {
    setForm({ ...form, hammers: [...form.hammers, e.target.value] });
  } else {
    setForm({
      ...form, hammers: form.hammers.filter((element) => element !== e.target.value),
    });
  }
};

And the checkbox

onChange={handleForm}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM