1

I have an onChange handler with which I can edit certain objects with input fields.

The onChange handler looks like this:

// First I get the current state
const [persons, setPersons] = useState(service.persons);

The API returns the following for persons (an array of objects):

contacts (2) [{…}, {…}]
0: {person_company: "Google", person_name: "John Doe"}
1: {person_company: "Facebook", person_name: "Jane Doe"}

My onChange handler looks like this (..first part of the if statement for editing the given persons works perfectly, but the else statement for creating a new person doesn't):

const updatePersons = (e: any, index?: number) => {
  if (index !== undefined) {
    const updatedPersons = [...persons];
    updatedPersons[index][e.target.name] = e.target.value;
    setPersons(updatedPersons);
  } else {
    setPersons({ ...persons, [e.target.name]: e.target.value });
  }
};

My template looks like this (just empty input fields for creating a new person with a person_company and person_name):

<InputContainer>
  <p>New person</p>
  <InputWrapper>
    <TextField
      type="text"
      label="Person company"
      name='person_company'
      value=' '
      onChange={e => updatePersons(e)}
    />
  </InputWrapper>
  <InputWrapper>
    <TextField
      type="text"
      label="Person name"
      name="person_name"
      value=' '
      onChange={e => updatePersons(e)}
    />
  </InputWrapper>
</InputContainer>

Unfortunately, it doesn't work that way.

  1. Only the first letter is realized in the first input field and the whole component disappears afterwards

  2. If I manipulate it so that the component does not disappear, the value of the input field is not updated (this is probably because I set value = ' '. I have no idea what else I should provide).

  3. If I console log persons after the first letter as mentioned in 1., the array of object is updated as follows:

    contacts {0: {…}, 1: {…}, company_name: " t"}
    0: {person_company: "Google", person_name: "John Doe"}
    1: {person_company: "Facebook", person_name: "Jane Doe"}
    person_company: " t"
    

But my goal is that the object should be updated as follows when I create a new person:

contacts (3) [{…}, {…}, {…}]
0: {person_company: "Google", person_name: "John Doe"}
1: {person_company: "Facebook", person_name: "Jane Doe"}
2: {person_company: "Amazon", person_name: "Max Mustermann"}

What am I doing wrong here?

2 Answers 2

2

try this dont spread a list inside of object, and instead spread a list and add object, also because when you call a new person there is no index provided :

const updatePersons = (e: any, index?: number) => {
  if (index !== undefined) {
    const updatedPersons = [...persons];
    updatedPersons[index][e.target.name] = e.target.value;
    setPersons(updatedPersons.map((person)=>({...person,new:false})));
  } else {
      const lastPerson = persons.slice(-1)[0];
      if (lastPerson.new){
          setPersons([...persons.slice(0,-1), {...lastPerson ,[e.target.name]:e.target.value}]);
      } else {
          setPersons([...persons,{[e.target.name]:e.target.value , new:true}]
      }
   }
};
Sign up to request clarification or add additional context in comments.

6 Comments

With this approach it adds a new object for every keystroke and I get the following after typing "test" in the person_company input field (and the value in the input field still doesn't update): codesandbox.io/s/ecstatic-fast-tfu60?file=/src/example.json
you also need to provide the index when you call onChange={e => updatePersons(e)} so onChange={e => updatePersons(e,index)}, or you need to think of other way to keep the 'new person' index even after incrementing the list size
please infrom me if went well... good luck
Unfortunately, this approach didn't work. But I solved it differently - I adapted the updatePersons method, which now only processes the cases with an ID (so no else block anymore). Instead, I now do everything directly in the onChange handler of the textfields to create a new person. I have also created a submitHandler in which I update the state and empty the form fields afterwards. I'll post the approach as an answer. Let me know if the solution is acceptable as well.
Good luck! I dont see a reason to post the answer but go for it if you wish..
|
0

Unfortunately, I couldn't solve it using the approaches suggested. But I did it in a different way.

I adapted the updatePersons method, which now only processes the cases with an ID (so no else block anymore).

const updatePersons = (e: any, index?: number) => {
  e.preventDefault(); // is this needed here?
  if (index !== undefined) {
    const updatedPersons = [...persons];
    updatedPersons[index][e.target.name] = e.target.value;
    setPersons(updatedPersons);
  }
};

Instead, I now do everything directly in the onChange handler of the textfields to create a new person.

// I created another useState hook for the new Person 
// ..and provided an initial state (empty strings) by instantiating a new Person:

const [newPerson, setNewPerson] = useState(new Person());

My textfields now look like this:

  <InputWrapper>
    <TextField
      type="text"
      label="Person name"
      name="person_name"
      value=' '
      onChange={e => setNewPerson({ ...newPerson, [e.target.name]: e.target.value })}
    />
  </InputWrapper>

I have also created a submit handler in which I update the state and empty the form fields afterwards.

  const submitHandler = (e: any) => {
    e.preventDefault(); // is this needed here?
    setPersons([...persons, newPerson]);
    setNewPerson(new Person()); // Empty the form fields
  };

Let me know if this solution is also ok.

1 Comment

Yes it does :).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.