简体   繁体   中英

ReactJS Dynamic input states

I need to add an "Add input" button which adds a <div> with some inputs. That works fine but if I remove some element (for example the first one) it will deletes the last one no matter what.

I was reading this question but I can't figure out how to bring it to my scenario.

I have created this Codesandbox to see "the bigger picture".

The main idea is whenever you click over "Add input" it adds that div to addedInputs state array:

function addInputElement(inputId) {
    const input = inputs[inputId];
    input.id = inputId;
    setAddedInputs([...addedInputs, input]);
    setSelectedValue("default");
    setAddInput(false);
}

When you write in any input of it, it adds it also to an object inputValue with the same index number as addedInputs array.

function changeInputValue(element, index, value) {
    const iv = inputValue;
    if (iv[index]) {
      Object.assign(iv[index], { [element]: value });
    } else {
      Object.assign(iv, { [index]: { [element]: value } });
    }
    setInputValue(iv);
}

But when I remove the input from addedInputs and it attempts to delete the content from inputValue it deletes the right addedInputs element but does not deletes the right inputValue element:

function removeInputElement(index) {
    const filteredInputs = addedInputs.filter((input, i) => i !== index);
    setAddedInputs(filteredInputs);
    if (inputValue[index] !== undefined) {
      const ivs = inputValue;
      delete ivs[index];
      setInputValue(ivs);
    }
}

Well, I am not sure if that is right approach for creating dynamic inputs states but, anyway my "error" here is that I am doing something wrong when removing an input value from inputValues . So, what am I doing wrong?

In case you want the entire code here:

import React from "react";

const inputs = {
  "123": { name: "Simple text input", type: "text" },
  "456": { name: "Simple number input", type: "number" }
};

export default function App() {
  const [addedInputs, setAddedInputs] = React.useState([]);
  const [inputValue, setInputValue] = React.useState({});
  const [selectedValue, setSelectedValue] = React.useState("default");
  const [addInput, setAddInput] = React.useState(false);

  function addInputElement(inputId) {
    const input = inputs[inputId];
    input.id = inputId;
    setAddedInputs([...addedInputs, input]);
    setSelectedValue("default");
    setAddInput(false);
  }

  function removeInputElement(index) {
    const filteredInputs = addedInputs.filter((input, i) => i !== index);
    setAddedInputs(filteredInputs);
    if (inputValue[index] !== undefined) {
      const ivs = inputValue;
      delete ivs[index];
      setInputValue(ivs);
    }
  }

  function changeInputValue(element, index, value) {
    const iv = inputValue;
    if (iv[index]) {
      Object.assign(iv[index], { [element]: value });
    } else {
      Object.assign(iv, { [index]: { [element]: value } });
    }
    setInputValue(iv);
  }

  return (
    <div className="App">
      {addedInputs.map((input, index) => (
        <div key={index}>
          <h5>{input.name}</h5>
          <input
            placeholder="Title"
            type="text"
            value={
              inputValue[index] &&
              inputValue[index][input.id] &&
              inputValue[index][input.id].title
            }
            onChange={(e) => {
              changeInputValue("title", index, e.target.value);
            }}
          />
          <input
            placeholder="Description"
            type="text"
            value={
              inputValue[index] &&
              inputValue[index][input.id] &&
              inputValue[index][input.id].title
            }
            onChange={(e) => {
              changeInputValue("description", index, e.target.value);
            }}
          />
          &nbsp;
          <button
            onClick={() => {
              removeInputElement(index);
            }}
          >
            X
          </button>
        </div>
      ))}
      {addInput && (
        <select
          defaultValue={selectedValue}
          onChange={(e) => {
            addInputElement(e.target.value);
          }}
        >
          <option disabled value="default">
            Select an option
          </option>
          {Object.keys(inputs).map((key, index) => {
            const input = inputs[key];
            return (
              <option key={index} value={key}>
                {input.name}
              </option>
            );
          })}
        </select>
      )}
      <button
        onClick={() => {
          setAddInput(true);
        }}
      >
        Add input
      </button>
    </div>
  );
}

** Overall Problems **

  1. Your code is a bit more complex than it has to be, which is the first cause of your bug(s). 1a. You have the addedInputs state variable, which tracks information about the inputs, but 1b. ...you also have the inputValue variable, which tracks multiple input values.
  2. Your naming conventions (the way that you decide what to call your variables & keys) could use some work. Here are two examples, although there are probably more. 2a. Your inputs variable. Inputs don't necessarily have names, titles & descriptions. You should name this variable after the actual, real-world object you are trying to model. 2b. Your inputValue variable. As I said above, this variable shouldn't exist, but if you want it to, please put an s at the end, because it does not track a single value , as the name would suggest, but rather all the values across your inputs.

Solution (to your immediate problem) Scrap the inputValue variable, and store each "input"'s value on that input.

Here's a working example in which I scrap the inputValue variable, making things much easier:

 import React from "react"; const inputs = { "123": { name: "Simple text input", type: "text" }, "456": { name: "Simple number input", type: "number" } }; export default function App() { const [addedInputs, setAddedInputs] = React.useState([]); const [selectedValue, setSelectedValue] = React.useState("default"); const [addInput, setAddInput] = React.useState(false); function addInputElement(inputId) { const input = inputs[inputId]; input.id = inputId; input.title = input.title || "A Default Title"; setAddedInputs([...addedInputs, input]); setSelectedValue("default"); setAddInput(false); } function removeInputElement(index) { const filteredInputs = addedInputs.filter((input, i) => i;== index); setAddedInputs(filteredInputs), } function changeInputValue(key, index. value) { const newAddedInput = {..,addedInputs[index]: [key]; value }. const newAddedInputs = [..;addedInputs]; newAddedInputs[index] = newAddedInput; setAddedInputs(newAddedInputs). } return ( <div className="App"> {addedInputs,map((input. index) => ( <div key={index}> <h5>{input.name}</h5> <input placeholder="Title" type="text" value={addedInputs[index] && addedInputs[index],title} onChange={(e) => { changeInputValue("title", index. e.target;value). }} /> <input placeholder="Description" type="text" value={addedInputs[index] && addedInputs[index],description} onChange={(e) => { changeInputValue("description", index. e.target;value); }} /> &nbsp; <button onClick={() => { removeInputElement(index). }} > X </button> </div> ))} {addInput && ( <select defaultValue={selectedValue} onChange={(e) => { addInputElement(e.target;value). }} > <option disabled value="default"> Select an option </option> {Object.keys(inputs),map((key; index) => { const input = inputs[key]. return ( <option key={index} value={key}> {input;name} </option> ); })} </select> )} <button onClick={() => { setAddInput(true); }} > Add input </button> </div> ); }

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