Search code examples
reactjsformsreact-hooksstate

Dynamically add or remove text input field and set values to an array of the main state in React JS


I am trying to figure out how to submit an arbitrary amount of array elements, so at least one to infinity.

This is a state that gets passed at the click of a submit button:

const [gallery, setGallery] = useState({
    title: "",
    description: "",
    image_url: [],
  });

I am looping the following state...

const [linkInput, setLinkInput] = useState([{ id: 1, link: "" }]);

...like so:

<div className="form-group mx-sm-3 mb-2">
          <label htmlFor="image_url">Image URLs*</label>
          {Array.isArray(linkInput) &&
            linkInput.map((input, i) => (
              <div className="input-group mb-3" key={input.id}>
                <input
                  className="form-control"
                  id="image_url"
                  type="text"
                  value={input.link}
                  required
                  onChange={(e) =>
                    onChangeLink({ ...input, link: e.target.value })
                  }
                />
                <br />
                <div style={{ color: "#ffffff" }}>__</div>
                {linkInput.length !== 1 && (
                  <>
                    <button
                      type="button"
                      className="btn btn-primary"
                      onClick={() => removeLink(input.id)}
                    >
                      Remove Image
                    </button>
                  </>
                )}
              </div>
            ))}
          <br />
          <button
            type="button"
            className="btn btn-primary"
            onClick={() => addInput()}
          >
            Add another image
          </button>
        </div>

These are functions I am using to add or remove input fields by modifying the "linkInput" state:

const handleRemoveLink = (id) => {
    setLinkInput(linkInput.filter((el) => el.id !== id));
};

  const handleAddInputField = () => {
    const lastItem = linkInput[linkInput.length - 1];
    setLinkInput([...linkInput, { id: Number(lastItem.id + 1), link: "" }]);
  };

I am trying to take the "link" value from each input element and place it in "image_url: []" upon clicking on submit button on my form. Currently I am not even close, the form behaves as such: it totally disappears when placing even a single character. What should I do?


Solution

  • You can do it by mapping over the existing linkInput array with map which returns a new array.

    const handleLink = (e, index) => {
      const result = linkInput.map((linkObj, i) => {
        if (i === index) {
          // create a new object with the values of the previous link object
          // set the link prop to the new value
          return {
            ...linkObj,
            link: e.target.value,
          };
        }
        return linkObj;
      });
      setLinkInput(result);
    };
    

    A common mistake is to not return a new object and mutate the linkObj directly. When doing so you'll change the original object.

    const linkInput = [{ id: 1, link: "" }];
    
    let result = [...linkInput];
    result = result.map((x, i) => {
      if (i === 0) x.link = "mutated";
      return x;
    });
    
    console.log('linkInput',linkInput);
    console.log('result',result);

    As you can see both of the array's have the "mutated" value while did copy the list.