1

I am trying to create a form for a user to submit a recipe. I need to get it set up to where the user can click the "+" button to add more ingredients or add more instructions. There's also a "-" button next to each input to remove it.

I have this mostly working, I can add items. However deleting the items does not seem to work with any consistency.

I am using Material UI, I am not certain if that is relevant.

Here are the relevant bits of code I am working on, I will link a full codesandbox at the end.

const [ingredients, setIngredients] = React.useState(
  [
    {
      name: "",
      amount: ""
    }
  ]
);

Here is how I am rendering the array in react

        {ingredients.map((ing, idx) => {
          return (
            <div key={idx}>
              <TextField
                id={"ing-name-" + idx}
                name={"ing-name-" + idx}
                variant="outlined"
                label="Ingrediant Name"
                value={ing.name}
                required
                onChange={handleIngredientChange}
              />
              <TextField
                id={"ing-amt-" + idx}
                name={"ing-amt-" + idx}
                variant="outlined"
                label="Ingredient Amount"
                value={ing.amount}
                required
                onChange={handleIngredientChange}
              />
              <Button
                id={"ing-remove-" + idx}
                variant="contained"
                color="secondary"
                type="button"
                onClick={handleIngredientRemove}
              >-</Button>
            </div>
          )
        })}

        <Button
          variant="contained"
          color="primary"
          type="button"
          onClick={handleIngredientAdd}
        >+</Button>

And finally here are the two functions handling the add / remove of the items


  function handleIngredientRemove(event) {
    /*
    * To remove an element, we just use the array.filter function to genereate a new array without the 
    * element being deleted
    */
    console.log(event.target.id);
    let idx = parseInt(event.target.id.split("-")[2]);
    console.log("Removing ingredient " + idx);
    let newIngredients = ingredients.filter((ingredient, index) => idx !== index);
    setIngredients(newIngredients);
    
  }

  function handleIngredientAdd(event) {
    /*
    * Same concept as the above methods, concat returns a new array. In this case we get a new array with an
    * element containing an empty string in both fields at the end of it
    */
    let newIngredients = ingredients.concat({name: "", amount: ""});
    setIngredients(newIngredients);
  }

Full codesandbox demo https://codesandbox.io/s/happy-herschel-k0oqf

3 Answers3

1

Material UI wraps your button with multiple tags. So when you click on a button, there might be something else inside was clicked.

Pass id to onClick function will solve your problem but it might create a new function every time when you click the button.

onClick={() => handleIngredientRemove(idx)}

I suggest you use currentTarget instead of current.

function handleInstructionRemove(event) {
    // Use event.currentTarget instead of event.current
    let idx = parseInt(event.currentTarget.id.split("-")[2]);
    console.log("Removing instruction " + idx);
    let newinstructions = instructions.filter(
          (instruction, index) => idx !== index
    );
    setInstructions(newinstructions);
 }
Anh Nhat Tran
  • 561
  • 1
  • 6
  • 18
  • Thanks, I've tried out both of these solutions and they both work. I think I will use the currentTarget like suggested. – Julian Dixon Nov 30 '20 at 16:07
0

A simple way would be to pass the index to the delete handler and filter with that index.(only changed for first form) here is the updated code

or you can use the timestamp as an id at the time of adding and using that unique timestamp id to delete

Abhishek
  • 89
  • 7
0

I would recommend not to parse element id to extract an index. You could pass it directly:

onClick={() => handleIngredientRemove(idx)}

Then catch it in handleIngredientRemove like

handleIngredientRemove(idx)

Now it deletes fine!