Romanas
Romanas

Reputation: 655

object of array not update in state

I have form where user can add as much as he wants documents. Each document have several inputs. And I'm trying to get each document inputs values and put it to state as array of objects. State should look like:

[
   {
      id: 'UGN2WP68P1',
      name: 'passport',
      placeIssue: 'paris'
   },
   {
      id: 'TD0KUWWIM6',
      name: 'shengen visa',
      placeIssue: 'paris'
   }
   ...
]

So I write a function which is called on inputs change. He check are there object with same id, if there is no object with same id he creates new and add to array, if object exist with same id then he update object:

const addNewDocumentObj = (id, type, val) => {
        // Get object with same id
        let docObj = addedDocsArr.filter( el => el.id === id)
        
        // If there is no object with same id, creates new one
        if (docObj.length === 0) {
            if (type === 'name'){
                let obj = {
                    id: id,
                    docId: val.id
                }
                setAddedDocsArr(addedDocsArr.concat(obj))
            } else if (type === 'placeIssue') {
                let obj = {
                    id: id,
                    placeIssue: val
                }
                setAddedDocsArr(addedDocsArr.concat(obj))
            }
        // If object exist with same id then updates with new value and adds to array
        } else {
            if (type === 'name'){
                let newObject = Object.assign(docObj, {name: val.id})
                let newArray = addedDocsArr.filter(el => el.id !== id)
                setAddedDocsArr(newArray.concat(newObject))
            } else if (type === 'placeIssue') {
                let newObject = Object.assign(docObj, {placeIssue: val})
                let newArray = addedDocsArr.filter(el => el.id !== id)
                setAddedDocsArr(newArray.concat(newObject))
            } 
        }
    }

But it doesn't work, and I can't understand why, maybe my logic is bad and there is better practise?

UPDATE: In React debugger I noticed how state changes. If I add select document name, in state object looks like that:

{name: 'passport', id: 'UGN2WP68P1'}

If I enter document place of issue value. Then object changes and show data like that:

{placeIssue: 'paris', id: 'UGN2WP68P1'}

But result should be:

{name: 'passport', placeIssue: 'paris', id: 'UGN2WP68P1'}

So it looks like that object not updated but created new one

Upvotes: 0

Views: 100

Answers (3)

Romanas
Romanas

Reputation: 655

I find a pretty easy way how to solve this problem. I read documentations of react forms and find multiple inputs idea React Forms So I changed my code to:

// Update or add information of added documents inputs
const addNewDocumentObj = (id, e, name) => {
        const newInput = addedDocsArr.map(el => {
            if(id === el.id) {
                if(name === 'name'){
                    el[name] = e.target.value
                } else if (name === 'placeIssue'){
                    el[name] = e.target.value
                }
            }
            return el
          })
          
          setAddedDocsArr(newInput);
    }

// Add new document inputs
    const addNewDocument = () => {
        let blockId = randomNumber(10, true, false)

        setAddedDocsArr([...addedDocsArr, {id: blockId, name: '', placeIssue: ''}])
    }

And it works perfectly!

Upvotes: 0

Ketan Ramteke
Ketan Ramteke

Reputation: 10675

Finished App:

enter image description here

Implementation of Handle submit:

const handleSubmit = (event) => {
    event.preventDefault();

    if (!uid) {
      alert("Please enter the ID");
      return;
    }
    let existingRecords = docs.filter((doc) => doc.id === uid);
    if (!existingRecords.length) {
      let newRecord = {
        id: uid,
        name: name,
        issuePlace: place
      };
      setDocs([...docs, newRecord]);
      setId("");
      setName("");
      setPlace("");
    } else {
      let unmodifiedRecords = docs.filter((doc) => doc.id !== uid);
      if (name) {
        existingRecords[0].name = name;
      }
      if (place) {
        existingRecords[0].issuePlace = place;
      }
      unmodifiedRecords.push(existingRecords[0]);
      setDocs(unmodifiedRecords);
      setId("");
      setName("");
      setPlace("");
    }

  };

And Here is the full finished example:

import React, { useState } from "react";
import "./styles.css";

export default function App() {
  const [docs, setDocs] = useState([
    { id: "1", name: "passport", issuePlace: "delhi" }
  ]);
  const [uid, setId] = useState("");
  const [name, setName] = useState("");
  const [place, setPlace] = useState("");

  const handleSubmit = (event) => {
    event.preventDefault();
    if (!uid) {
      alert("Please enter the ID");
      return;
    }
    let existingRecords = docs.filter((doc) => doc.id === uid);
    if (!existingRecords.length) {
      let newRecord = {
        id: uid,
        name: name,
        issuePlace: place
      };
      setDocs([...docs, newRecord]);
      setId("");
      setName("");
      setPlace("");
    } else {
      let unmodifiedRecords = docs.filter((doc) => doc.id !== uid);
      if (name) {
        existingRecords[0].name = name;
      }
      if (place) {
        existingRecords[0].issuePlace = place;
      }
      unmodifiedRecords.push(existingRecords[0]);
      setDocs(unmodifiedRecords);
      setId("");
      setName("");
      setPlace("");
    }
  };
  return (
    <div className="App">
      <form onSubmit={handleSubmit}>
        <table>
          <tr>
            <td>
              <label>ID: </label>
            </td>
            <td>
              <input
                value={uid}
                onChange={(e) => {
                  setId(e.target.value);
                }}
              />
            </td>
          </tr>

          <tr>
            <td>
              <label>Name: </label>
            </td>
            <td>
              <input
                value={name}
                onChange={(e) => {
                  setName(e.target.value);
                }}
              />
            </td>
          </tr>

          <tr>
            <td>
              <label>Isuue Place: </label>
            </td>
            <td>
              <input
                value={place}
                onChange={(e) => {
                  setPlace(e.target.value);
                }}
              />
            </td>
          </tr>
          <tr>
            <td>
              <button type="submit">Submit</button>
            </td>
          </tr>
        </table>
      </form>

      {docs.map((doc) => (
        <div className="records">
          <span>{"ID:" + doc.id + "   "}</span>
          <span>{"Name:" + doc.name + "   "}</span>
          <span>{"Issue Place:" + doc.issuePlace + "   "}</span>
        </div>
      ))}
    </div>
  );
}

Check out finished example with source code at: Codesandbox Link

Upvotes: 0

nate river
nate river

Reputation: 101

Maybe you need something like:

const addNewDocumentObj = (id, type, val) => {
  // Get object with same id
  let docObj = addedDocsArr.find(el => el.id === id)  
  // If there is no object with same id, creates new one
  if (!docObj) {
    docObj = { id, placeIssue: val }
    // and pushes it to addedDocsArray
    addedDocsArr.push(docObj)
  }

  if (type === 'name') {
    docObj.name = val.id
  } else if (type === 'placeIssue') {
    docObj.placeIssue = val
  }

  setAddedDocsArr(addedDocsArr)
}

First of all, why are you using filter if you are actually try to find something in array? Just use find.

Second, if object with given id is already exists, there is no need to filter your array and then put that object back... Just find that object in array and update it! It is already in your array! Remember that Array contains references to your objects, so when you grab your object from the Array and edit it, your edit the same object that Array have.

Last one, Idk what logic your setAddedDocsArr function have. In my example I assume that the only thing it does is set its argument (newArray) to the variable named addedDocsArr. So instead of that, in situation where object with given id is not present, I just push it in old array.

Upvotes: 1

Related Questions