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

Time:02-04

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?

CodePudding user response:

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.

CodePudding user response:

The solution was to specify exactly what event corresponds to what array element:

const handleLink = (e, index) => {
    let result = [...linkInput];
    result = result.map((x, i) => {
      if (i === index) x.link = e.target.value;
      return x;
    });
    setLinkInput(result);
  };

NOTE: I dropped "id" property on linkInput in favor of indexes.

  • Related