简体   繁体   中英

Add and Remove input component on click of a Button in ReactJS

I tried to implement a form with an input box that allows user to add/remove text input components on click of a button, here is what I have so far,

const UserInput = () => {
      const selectValues = ['Value 1', 'Value 2'];
      const [textBox, setTextBox] = useState(selectValues);
      const addNewTextInput = () => {
            const newTextValue = 'Value' + textBox.length;
            setTextBox([...textBox, newTextValue])
      };
     const removeInput = (index: number) => {
           const textInput = [...textBox];
           textInput.splice(index, 1);
           setTextBox(textInput);
     };
     return (
            <div>
                 <button onClick={addInput}>+</button>
                 <button onClick={removeInput}>+</button>
                 {textBox.map((value, i) => {
            return (
                    <input
                     onChange={handleChange}
                     value={value}
                     id={i}
                     type={type}
                     />
            );
            </div>
            })}
    )
 }

Now, I am able to add a new text input and also delete the input successfully, but when I delete, instead of the recently added input, the very first input field is being deleted. I am not sure if there is anything wrong with the removeInput function. Also, I have declared handleChange function in a different file.

you have to delted last element, so try this sending last index number instead first,

textInput.splice(0, -1);

this way it will work,

 const removeInput = () => {
       const textInput = [...textBox];
       textInput.splice(0, -1);
       setTextBox(textInput);
 };

There's a few problems with your code, but the one which is causing your issue is when you are slicing the array.

Splicing issue

In removeInput() , you take in an index argument, but when you give the handler to onClick , you don't give it an index. That means index is the event object of that handler ( docs on React events ) which is not a number. In this scenario, the splice() call defaults to removing the first element. You need to do the following:

const removeInput = () => {
  // This is the proper way to change state that depends on previous state.
  setTextBox((prevTextBox) => {
    const mutatableTextBox = [...prevTextBox];
    mutatableTextBox.splice(mutatableTextBox.length-1, 1);
    return mutatableTextBox;
  })}

Possible key issue

In your example you are also rendering the list of items without passing a key property. This property is used under the hood by React to help it efficiently render lists of components. When you don't give a key prop, React defaults to using the index (i). Here's the documentation for lists and keys in the React docs.

This could be a problem when you're rendering your inputs, as the indices aren't changing in React's eyes. See the below example for clarification.

{["a", "b", "c"].map((letter) => <p>{letter}</p>)}

// React renders
<p>a</p>
<p>b</p>
<p>c</p>

// Change state and remove the value at index 1 (b)
{["a", "c"].map((letter) => <p>{letter}</p>)}

// React will now render:
<p>a</p>
<p>b</p>

Why? The indexes have not changed. React automatically gives the 'a' paragraph an index of 0, 'b' has 1 and 'c' has 2. When we change the array, React sees that we still have index 0 and 1, so it thinks we're still using 'a' and 'b', but we're not. I hope you can see how this might be an issue.

Technically it won't matter for your code, as you always delete the last index, but it is best practice to do it like this:

const UserInput = () => {
  // Math.random() is okay for this purpose, but a better alternative would
  // be to use custom keys which are 100% guaranteed to be unique.
  const selectValues = [
    {text: "Value 1", key: Math.random()},
    {text: "Value 2", key: Math.random()}
  ];
  const [textBox, setTextBox] = useState(selectValues);

  const addNewTextInput = () => {
    setTextBox((prevTextBox) => {
      const newTextValue = 'Value' + prevTextBox.length;
      return [...prevTextBox, {text: newTextValue, key: Math.random()}];
    });
  };

  const removeInput = () => {
  // This is the proper way to change state that depends on previous state.
  setTextBox((prevTextBox) => {
    const mutatableTextBox = [...prevTextBox];
    mutatableTextBox.splice(mutatableTextBox.length-1, 1);
    return mutatableTextBox;
  })}

  return (
    <div>
      <button onClick={addInput}>+</button>
      <button onClick={removeInput}>+</button>
      {textBox.map((value, i) => {
         return (
           <input
              onChange={handleChange}
              value={value.text}
              key={value.key}
              id={i}
              type={type}
           />
         );
      })}
    </div>      
  )
 }

I hope this helped, let me know if you have any more issues.

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