3

The fact is I'm using an object to store all the data my form collects. In this object, I have an array, and the only solution I've found to update it is to create a function that adds every input into an array and then sets : this.setState({ 'invites': array }); But, the problem is that every change is added to the array and stored. How can I fix this ?

<input
    className="form-input"
    type="email"
    placeholder="nom@exemple.com"
    name="invites"
    onChange={e => addToArray(e, "invites")}
    required
/>
function addToArray(e, name) {
     emailList.push(e.target.value);
     props.handleChangeArray(name, emailList);
}
handleChangeArray = (name, array) => {
    this.setState({ [name]: array });
};
dance2die
  • 35,807
  • 39
  • 131
  • 194
M Millo
  • 47
  • 5

2 Answers2

4

EDIT:

In the StackSnippet below, I have updated your example (OPs example can be found here) showing how you can get values on submit:

const { useState } = React;

const WORD_LIST = ["Foo", "Bar", "Baz", "Rock", "Paper", "Scissors", "Hello", "Goodbye"];

function InviteInput(props) {
  const { value, onChange } = props;
  const handleChange = e => {
    onChange && onChange(e);
  };
  return (
    <li>
      <input
        value={value}
        onChange={handleChange}
        className="form-input"
        type="email"
        placeholder="nom@exemple.com"
        name="invites"
        required
      />
    </li>
  );
}

function AddInviteButton(props) {
  return (
    <button onClick={props.onClick}>
      Ajouter une autre personne // (Add another person)
    </button>
  );
}

function InviteForm({ onSubmit, initialInputCount }) {
  const [nbInvites, setNbInvites] = useState(
    [...Array(initialInputCount).keys()].map(i=>WORD_LIST[i])
  );

  const onAddInviteClick = () => {
    //let id = nbInvites.length + 1;
    setNbInvites([
      ...nbInvites,
      //{
        //id,
        //***********************************************************
        // THIS IS WHERE YOU SET THE DEFAULT VALUE FOR NEW INPUTS
        //***********************************************************
        /*value:*/ WORD_LIST[Math.floor(Math.random() * WORD_LIST.length)]
        //***********************************************************
      //}
    ]);
  };

  const handleChange = (event, index) => {
    let newstate = [...nbInvites];
    newstate[index]/*.value*/ = event.target.value;
    setNbInvites(newstate);
  };

  const handleSubmit = event => {
    onSubmit(event, nbInvites);
  };

  return (
    <div>
      {nbInvites.map((item, index) => {
        return (
          <InviteInput
            key={index}
            value={item}
            onChange={e => handleChange(e, index)}
          />
        );
      })}
      <AddInviteButton onClick={onAddInviteClick} />
      <br />
      <button onClick={handleSubmit}>Soumettre // Submit</button>
    </div>
  );
}

function App() {
  const [formVals, setFormVals] = useState();

  const doSubmit = (event, formValues) => {
    setFormVals(formValues);
  };

  return (
    <div className="page">
      <h2 className="box-title">
        Qui sont les eleves de cette classe ? // (Who are the students in this
        class?)
      </h2>
      <p>
        Vous pourrez toujours en ajouter par la suite // (You can always add
        some later)
      </p>
      <InviteForm onSubmit={doSubmit} initialInputCount={5} />
      {formVals ? <pre>{JSON.stringify(formVals, null, 2)}</pre> : ""}
    </div>
  );
}

ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>

ORIGINAL ANSWER:

You'll need to use the value prop and use event.target.value to assign the value of whatever is typed to the input.

Something like this:

function Test() {
  const [inputs, setInputs] = React.useState([{
    id: 1,
    value: "1 initial"
  }, {
    id: 2,
    value: "2 initial"
  }]);
  
  const handleChange = (event, index) => {
    let newstate = [...inputs];
    newstate[index].value = event.target.value;
    setInputs(newstate);
  }
  
  const addInput = () => {
    let id = inputs.length + 1;
    setInputs([...inputs, {
      id,
      value: `${id} initial`
    }])
  }
  
  return(
    <div>
      <button onClick={addInput}>Add Input</button>
      {inputs.map((item, index) => {
        return <div><input type="text" value={inputs[index].value} onChange={e=>handleChange(e,index)} /></div>
      })}
    </div>
  );
}

ReactDOM.render(<Test />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
Matt Oestreich
  • 8,219
  • 3
  • 16
  • 41
1

Instead of onChange, we can use onKeyUp to identify user typing complete event,

We can use onKeyUp event to identify user input event and debounce is to identify when user completes entering all the keywords, we need to use loadash for this functionality

It's just one line with underscore.js debounce function:

onKeyUp={e => _.debounce(addToArray(e, "invites") , 500)}

This basically says doSomething 500 milliseconds after I stop typing.

For more info: http://underscorejs.org/#debounce

Updated the above line

<input
    className="form-input"
    type="email"
    placeholder="nom@exemple.com"
    name="invites"
    onKeyUp={e => _.debounce(addToArray(e, "invites") , 500)}
    required
/>
function addToArray(e, name) {
     emailList.push(e.target.value);
     props.handleChangeArray(name, emailList);
}
handleChangeArray = (name, array) => {
    this.setState({ [name]: array });
};

Source - Run javascript function when user finishes typing instead of on key up?

Kannan G
  • 974
  • 6
  • 9